Repository: linkerd/linkerd2
Pull request #4193 (2020-03-23 23:12:40)

Add missing PSP for linkerd-smi-metrics
The linkerd-smi-metrics ServiceAccount wasn't hooked into linkerd's PSP
resource, which resulted in the linkerd-smi-metrics ReplicaSet failing
to spawn pods:

```
Error creating: pods "linkerd-smi-metrics-574f57ffd4-" is forbidden:
unable to validate against any pod security policy: []
```

Edit: Obviously, this only happens in PSP-enabled clusters...

alpeb: > should there be some kind of automated test for this?

It seems KinD doesn't play very well with PSP, but I'll investigate more. 

Patch:
diff --git a/Dockerfile-proxy b/Dockerfile-proxy
index fd1a9a508..b758d67db 100644
--- a/Dockerfile-proxy
+++ b/Dockerfile-proxy
@@ -9,7 +9,7 @@ RUN (proxy=$(bin/fetch-proxy $(cat proxy-version)) && \
     mv "$proxy" linkerd2-proxy)
 
 ## compile proxy-identity agent
-FROM gcr.io/linkerd-io/go-deps:6b9a8ea4 as golang
+FROM gcr.io/linkerd-io/go-deps:1b3f62c3 as golang
 WORKDIR /linkerd-build
 COPY pkg/flags pkg/flags
 COPY pkg/tls pkg/tls
diff --git a/charts/linkerd2/templates/psp.yaml b/charts/linkerd2/templates/psp.yaml
index 98613c80d..1c3fb3b9c 100644
--- a/charts/linkerd2/templates/psp.yaml
+++ b/charts/linkerd2/templates/psp.yaml
@@ -116,3 +116,6 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: {{.Values.global.namespace}}
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: {{.Values.global.namespace}}
diff --git a/cli/Dockerfile-bin b/cli/Dockerfile-bin
index fe5542c4c..32957f83e 100644
--- a/cli/Dockerfile-bin
+++ b/cli/Dockerfile-bin
@@ -1,5 +1,5 @@
 ## compile binaries
-FROM gcr.io/linkerd-io/go-deps:6b9a8ea4 as golang
+FROM gcr.io/linkerd-io/go-deps:1b3f62c3 as golang
 WORKDIR /linkerd-build
 COPY cli cli
 COPY charts charts
diff --git a/cni-plugin/Dockerfile b/cni-plugin/Dockerfile
index 69e39fbfa..c812258ea 100644
--- a/cni-plugin/Dockerfile
+++ b/cni-plugin/Dockerfile
@@ -1,5 +1,5 @@
 ## compile cni-plugin utility
-FROM gcr.io/linkerd-io/go-deps:6b9a8ea4 as golang
+FROM gcr.io/linkerd-io/go-deps:1b3f62c3 as golang
 WORKDIR /linkerd-build
 COPY pkg pkg
 COPY controller controller
diff --git a/controller/Dockerfile b/controller/Dockerfile
index 21dcac40d..2c6684e4c 100644
--- a/controller/Dockerfile
+++ b/controller/Dockerfile
@@ -1,5 +1,5 @@
 ## compile controller service
-FROM gcr.io/linkerd-io/go-deps:6b9a8ea4 as golang
+FROM gcr.io/linkerd-io/go-deps:1b3f62c3 as golang
 WORKDIR /linkerd-build
 COPY controller/gen controller/gen
 COPY pkg pkg
diff --git a/controller/api/destination/endpoint_translator.go b/controller/api/destination/endpoint_translator.go
index c4acdbc7c..7a1811699 100644
--- a/controller/api/destination/endpoint_translator.go
+++ b/controller/api/destination/endpoint_translator.go
@@ -49,12 +49,20 @@ func (et *endpointTranslator) Add(set watcher.AddressSet) {
 		if address.Pod != nil {
 			wa, err = et.toWeightedAddr(address)
 		} else {
+			var authOverride *pb.AuthorityOverride
+			if address.AuthorityOverride != "" {
+				authOverride = &pb.AuthorityOverride{
+					AuthorityOverride: address.AuthorityOverride,
+				}
+			}
+
 			// handling address with no associated pod
 			var addr *net.TcpAddress
 			addr, err = et.toAddr(address)
 			wa = &pb.WeightedAddr{
-				Addr:   addr,
-				Weight: defaultWeight,
+				Addr:              addr,
+				Weight:            defaultWeight,
+				AuthorityOverride: authOverride,
 			}
 
 			if address.Identity != "" {
@@ -65,7 +73,7 @@ func (et *endpointTranslator) Add(set watcher.AddressSet) {
 						},
 					},
 				}
-				// in this case we most likely have a proxy on the side, so set protocol hint as well.
+				// in this case we most likely have a proxy on the other side, so set protocol hint as well.
 				if et.enableH2Upgrade {
 					wa.ProtocolHint = &pb.ProtocolHint{
 						Protocol: &pb.ProtocolHint_H2_{
diff --git a/controller/api/destination/watcher/endpoints_watcher.go b/controller/api/destination/watcher/endpoints_watcher.go
index fd2f6b450..6dfb400e2 100644
--- a/controller/api/destination/watcher/endpoints_watcher.go
+++ b/controller/api/destination/watcher/endpoints_watcher.go
@@ -31,12 +31,13 @@ type (
 	// case that this endpoint is not associated with a pod and maps
 	// to some other IP (i.e. a remote service gateway)
 	Address struct {
-		IP        string
-		Port      Port
-		Pod       *corev1.Pod
-		OwnerName string
-		OwnerKind string
-		Identity  string
+		IP                string
+		Port              Port
+		Pod               *corev1.Pod
+		OwnerName         string
+		OwnerKind         string
+		Identity          string
+		AuthorityOverride string
 	}
 
 	// AddressSet is a set of Address, indexed by ID.
@@ -90,6 +91,7 @@ type (
 	portPublisher struct {
 		id         ServiceID
 		targetPort namedPort
+		srcPort    Port
 		hostname   string
 		log        *logging.Entry
 		k8sAPI     *k8s.API
@@ -392,6 +394,7 @@ func (sp *servicePublisher) newPortPublisher(srcPort Port, hostname string) *por
 	port := &portPublisher{
 		listeners:  []EndpointUpdateListener{},
 		targetPort: targetPort,
+		srcPort:    srcPort,
 		hostname:   hostname,
 		exists:     exists,
 		k8sAPI:     sp.k8sAPI,
@@ -465,10 +468,16 @@ func (pp *portPublisher) endpointsToAddresses(endpoints *corev1.Endpoints) Addre
 					Namespace: endpoints.ObjectMeta.Namespace,
 				}
 
+				var authorityOverride string
+				if fqName, ok := endpoints.Annotations[consts.RemoteServiceFqName]; ok {
+					authorityOverride = fmt.Sprintf("%s:%d", fqName, pp.srcPort)
+				}
+
 				addresses[id] = Address{
-					IP:       endpoint.IP,
-					Port:     resolvedPort,
-					Identity: endpoints.Annotations[consts.RemoteGatewayIdentity],
+					IP:                endpoint.IP,
+					Port:              resolvedPort,
+					Identity:          endpoints.Annotations[consts.RemoteGatewayIdentity],
+					AuthorityOverride: authorityOverride,
 				}
 				continue
 			}
diff --git a/controller/cmd/service-mirror/cluster_watcher.go b/controller/cmd/service-mirror/cluster_watcher.go
index 782be2b0f..ffedf91c1 100644
--- a/controller/cmd/service-mirror/cluster_watcher.go
+++ b/controller/cmd/service-mirror/cluster_watcher.go
@@ -76,6 +76,7 @@ type (
 		gatewayData          *gatewayMetadata
 		newResourceVersion   string
 		affectedServices     []*corev1.Service
+		identity             string
 	}
 
 	// ConsiderGatewayUpdateDispatch is issued when we are receiving an update for a
@@ -142,9 +143,9 @@ func (re RetryableError) Error() string {
 	return fmt.Sprintf("Inner errors:\n\t%s", strings.Join(errorStrings, "\n\t"))
 }
 
-func (rcsw *RemoteClusterServiceWatcher) extractGatewayInfo(gateway *corev1.Service) ([]corev1.EndpointAddress, int32, string, error) {
+func (rcsw *RemoteClusterServiceWatcher) extractGatewayInfo(gateway *corev1.Service) ([]corev1.EndpointAddress, int32, string, string, error) {
 	if len(gateway.Status.LoadBalancer.Ingress) == 0 {
-		return nil, 0, "", errors.New("expected gateway to have at lest 1 external Ip address but it has none")
+		return nil, 0, "", "", errors.New("expected gateway to have at lest 1 external Ip address but it has none")
 	}
 
 	var foundPort = false
@@ -158,7 +159,7 @@ func (rcsw *RemoteClusterServiceWatcher) extractGatewayInfo(gateway *corev1.Serv
 	}
 
 	if !foundPort {
-		return nil, 0, "", fmt.Errorf("cannot find  port named %s on gateway", consts.GatewayPortName)
+		return nil, 0, "", "", fmt.Errorf("cannot find  port named %s on gateway", consts.GatewayPortName)
 	}
 
 	var gatewayEndpoints []corev1.EndpointAddress
@@ -168,16 +169,19 @@ func (rcsw *RemoteClusterServiceWatcher) extractGatewayInfo(gateway *corev1.Serv
 			Hostname: ingress.Hostname,
 		})
 	}
-	return gatewayEndpoints, port, gateway.ResourceVersion, nil
+
+	gatewayIdentity := gateway.Annotations[consts.GatewayIdentity]
+
+	return gatewayEndpoints, port, gateway.ResourceVersion, gatewayIdentity, nil
 }
 
 // When the gateway is resolved we need to produce a set of endpoint addresses that that
 // contain the external IPs that this gateway exposes. Therefore we return the IP addresses
 // as well as a single port on which the gateway is accessible.
-func (rcsw *RemoteClusterServiceWatcher) resolveGateway(metadata *gatewayMetadata) ([]corev1.EndpointAddress, int32, string, error) {
+func (rcsw *RemoteClusterServiceWatcher) resolveGateway(metadata *gatewayMetadata) ([]corev1.EndpointAddress, int32, string, string, error) {
 	gateway, err := rcsw.remoteAPIClient.Svc().Lister().Services(metadata.Namespace).Get(metadata.Name)
 	if err != nil {
-		return nil, 0, "", err
+		return nil, 0, "", "", err
 	}
 	return rcsw.extractGatewayInfo(gateway)
 }
@@ -401,7 +405,7 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteServiceUpdated(ev *RemoteSe
 	serviceInfo := fmt.Sprintf("%s/%s", ev.remoteUpdate.Namespace, ev.remoteUpdate.Name)
 	rcsw.log.Debugf("Updating remote mirrored service %s/%s", ev.localService.Namespace, ev.localService.Name)
 
-	gatewayEndpoints, gatewayPort, resVersion, err := rcsw.resolveGateway(ev.gatewayData)
+	gatewayEndpoints, gatewayPort, resVersion, gatewayIdentity, err := rcsw.resolveGateway(ev.gatewayData)
 	if err == nil {
 		ev.localEndpoints.Subsets = []corev1.EndpointSubset{
 			{
@@ -413,6 +417,12 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteServiceUpdated(ev *RemoteSe
 		ev.localEndpoints.Labels[consts.RemoteGatewayNameLabel] = ev.gatewayData.Name
 		ev.localEndpoints.Labels[consts.RemoteGatewayNsLabel] = ev.gatewayData.Namespace
 
+		if gatewayIdentity != "" {
+			ev.localEndpoints.Annotations[consts.RemoteGatewayIdentity] = gatewayIdentity
+		} else {
+			delete(ev.localEndpoints.Annotations, consts.RemoteGatewayIdentity)
+		}
+
 	} else {
 		rcsw.log.Warnf("Could not resolve gateway for %s: %s, nulling endpoints", serviceInfo, err)
 		ev.localEndpoints.Subsets = nil
@@ -479,11 +489,14 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteServiceCreated(ev *RemoteSe
 				consts.RemoteGatewayNameLabel: ev.gatewayData.Name,
 				consts.RemoteGatewayNsLabel:   ev.gatewayData.Namespace,
 			},
+			Annotations: map[string]string{
+				consts.RemoteServiceFqName: fmt.Sprintf("%s.%s.svc.%s", remoteService.Name, remoteService.Namespace, rcsw.clusterDomain),
+			},
 		},
 	}
 
 	// Now we try to resolve the remote gateway
-	gatewayEndpoints, gatewayPort, resVersion, err := rcsw.resolveGateway(ev.gatewayData)
+	gatewayEndpoints, gatewayPort, resVersion, gatewayIdentity, err := rcsw.resolveGateway(ev.gatewayData)
 	if err == nil {
 		// only if we resolve it, we are updating the endpoints addresses and ports
 		rcsw.log.Debugf("Resolved remote gateway [%v:%d] for %s", gatewayEndpoints, gatewayPort, serviceInfo)
@@ -493,9 +506,10 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteServiceCreated(ev *RemoteSe
 				Ports:     rcsw.getEndpointsPorts(ev.service, gatewayPort),
 			},
 		}
-
 		serviceToCreate.Annotations[consts.RemoteGatewayResourceVersionAnnotation] = resVersion
-
+		if gatewayIdentity != "" {
+			endpointsToCreate.Annotations[consts.RemoteGatewayIdentity] = gatewayIdentity
+		}
 	} else {
 		rcsw.log.Warnf("Could not resolve gateway for %s: %s, skipping subsets", serviceInfo, err)
 		endpointsToCreate.Subsets = nil
@@ -570,6 +584,13 @@ func (rcsw *RemoteClusterServiceWatcher) handleRemoteGatewayUpdated(ev *RemoteGa
 				Ports:     rcsw.getEndpointsPorts(updatedService, ev.newPort),
 			},
 		}
+
+		if ev.identity != "" {
+			updatedEndpoints.Annotations[consts.RemoteGatewayIdentity] = ev.identity
+		} else {
+			delete(updatedEndpoints.Annotations, consts.RemoteGatewayIdentity)
+		}
+
 		_, err = rcsw.localAPIClient.Client.CoreV1().Services(updatedService.Namespace).Update(updatedService)
 		if err != nil {
 			errors = append(errors, err)
@@ -624,7 +645,7 @@ func (rcsw *RemoteClusterServiceWatcher) handleConsiderGatewayUpdateDispatch(eve
 			Name:      event.maybeGateway.Name,
 			Namespace: event.maybeGateway.Namespace,
 		}
-		if endpoints, port, resVersion, err := rcsw.extractGatewayInfo(event.maybeGateway); err != nil {
+		if endpoints, port, resVersion, identity, err := rcsw.extractGatewayInfo(event.maybeGateway); err != nil {
 			rcsw.log.Warnf("Gateway [%s/%s] is not a compliant gateway anymore, dispatching GatewayDeleted event: %s", event.maybeGateway.Namespace, event.maybeGateway.Name, err)
 			// in case something changed about this gateway and it is not really a gateway anymore,
 			// simply dispatch deletion event so all endpoints are nulled
@@ -646,6 +667,7 @@ func (rcsw *RemoteClusterServiceWatcher) handleConsiderGatewayUpdateDispatch(eve
 					gatewayData:          gatewayMeta,
 					newResourceVersion:   resVersion,
 					affectedServices:     affectedServices,
+					identity:             identity,
 				})
 			}
 
diff --git a/go.mod b/go.mod
index 230c5509b..4d1761920 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/huandu/xstrings v1.2.0 // indirect
 	github.com/imdario/mergo v0.3.7
 	github.com/julienschmidt/httprouter v1.2.0
-	github.com/linkerd/linkerd2-proxy-api v0.1.10
+	github.com/linkerd/linkerd2-proxy-api v0.1.12
 	github.com/linkerd/linkerd2-proxy-init v1.3.2
 	github.com/mattn/go-isatty v0.0.9
 	github.com/mattn/go-runewidth v0.0.2
diff --git a/go.sum b/go.sum
index 57e1e409a..f13c3cfc2 100644
--- a/go.sum
+++ b/go.sum
@@ -211,6 +211,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/linkerd/linkerd2-proxy-api v0.1.10 h1:zGwz2Fek7fdfCxyZW7jq8IE6sexeVFW5UFBQFL6MrP4=
 github.com/linkerd/linkerd2-proxy-api v0.1.10/go.mod h1:2WJHEYXoww5ALM6c1QspRFiROGZg08tGxCO1js0tTVA=
+github.com/linkerd/linkerd2-proxy-api v0.1.12-0.20200309112816-75a7b3b20597 h1:HIzyna4iqxvaBgLVicTvoiBJjWlgqe28iBDygTG14lA=
+github.com/linkerd/linkerd2-proxy-api v0.1.12-0.20200309112816-75a7b3b20597/go.mod h1:2WJHEYXoww5ALM6c1QspRFiROGZg08tGxCO1js0tTVA=
+github.com/linkerd/linkerd2-proxy-api v0.1.12 h1:mP0UYjmemLI6Ru7m1HW3d9up5Bt9VRB9FsxRA8jy2IM=
+github.com/linkerd/linkerd2-proxy-api v0.1.12/go.mod h1:2WJHEYXoww5ALM6c1QspRFiROGZg08tGxCO1js0tTVA=
 github.com/linkerd/linkerd2-proxy-init v1.3.1 h1:KTvUvPlSKUkpzZM0L/EIk+EJaKk7dwygu0CP95fOyGI=
 github.com/linkerd/linkerd2-proxy-init v1.3.1/go.mod h1:lK/sORPZDP6aFhPZps+AHMKphX5v9bKVDNESllz18C0=
 github.com/linkerd/linkerd2-proxy-init v1.3.2 h1:bl+gwdBtHI0cki/urTyicxxb/ekQtVWJ6NDWzMXAgVs=
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index fa3a54c6b..a5172baec 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -160,11 +160,11 @@ const HintBaseURL = "https://linkerd.io/checks/#"
 
 // AllowedClockSkew sets the allowed skew in clock synchronization
 // between the system running inject command and the node(s), being
-// based on assumed node's heartbeat interval (<= 60 seconds) plus default TLS
+// based on assumed node's heartbeat interval (5 minutes) plus default TLS
 // clock skew allowance.
 //
 // TODO: Make this default value overridiable, e.g. by CLI flag
-const AllowedClockSkew = time.Minute + tls.DefaultClockSkewAllowance
+const AllowedClockSkew = 5*time.Minute + tls.DefaultClockSkewAllowance
 
 var linkerdHAControlPlaneComponents = []string{
 	"linkerd-controller",
@@ -181,10 +181,12 @@ var (
 
 	expectedServiceAccountNames = []string{
 		"linkerd-controller",
+		"linkerd-destination",
 		"linkerd-grafana",
 		"linkerd-identity",
 		"linkerd-prometheus",
 		"linkerd-proxy-injector",
+		"linkerd-smi-metrics",
 		"linkerd-sp-validator",
 		"linkerd-web",
 		"linkerd-tap",
@@ -499,6 +501,7 @@ func (hc *HealthChecker) allCategories() []category {
 				{
 					description: "no clock skew detected",
 					hintAnchor:  "pre-k8s-clock-skew",
+					warning:     true,
 					check: func(context.Context) error {
 						return hc.checkClockSkew()
 					},
diff --git a/pkg/k8s/labels.go b/pkg/k8s/labels.go
index ac8ebeba9..92ad276d9 100644
--- a/pkg/k8s/labels.go
+++ b/pkg/k8s/labels.go
@@ -377,6 +377,9 @@ const (
 	// RemoteGatewayIdentity follows the same kind of logic as RemoteGatewayNameLabel
 	RemoteGatewayIdentity = SvcMirrorPrefix + "/remote-gateway-identity"
 
+	// GatewayIdentity can be found on the remote gateway service
+	GatewayIdentity = SvcMirrorPrefix + "/gateway-identity"
+
 	// ConfigKeyName is the key in the secret that stores the kubeconfig needed to connect
 	// to a remote cluster
 	ConfigKeyName = "kubeconfig"
diff --git a/web/Dockerfile b/web/Dockerfile
index c06e9eb4d..d123ba527 100644
--- a/web/Dockerfile
+++ b/web/Dockerfile
@@ -21,7 +21,7 @@ COPY web/app ./web/app
 RUN ./bin/web build
 
 ## compile go server
-FROM gcr.io/linkerd-io/go-deps:6b9a8ea4 as golang
+FROM gcr.io/linkerd-io/go-deps:1b3f62c3 as golang
 WORKDIR /linkerd-build
 RUN mkdir -p web
 COPY web/main.go web


Test patch:
diff --git a/cli/cmd/testdata/install_config.golden b/cli/cmd/testdata/install_config.golden
index 405002dc2..22156813d 100644
--- a/cli/cmd/testdata/install_config.golden
+++ b/cli/cmd/testdata/install_config.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index 3c2b2d98a..11f470b9f 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index e35f36f35..1cf5d895e 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index 88e0e1dd2..8a4828401 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index 03f0b3b10..a3cf52bb7 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index 2096b6c2e..3a7f35934 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index c2fb601c1..335100ab0 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -793,6 +793,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_helm_output.golden b/cli/cmd/testdata/install_helm_output.golden
index 16b659f52..a94e4796d 100644
--- a/cli/cmd/testdata/install_helm_output.golden
+++ b/cli/cmd/testdata/install_helm_output.golden
@@ -865,6 +865,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 # Source: linkerd2/templates/smi-metrics-rbac.yaml
 ---
diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden
index c288aee05..14ed94d3b 100644
--- a/cli/cmd/testdata/install_helm_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_output_ha.golden
@@ -865,6 +865,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 # Source: linkerd2/templates/smi-metrics-rbac.yaml
 ---
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index baf2a4291..0e7e4154c 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -834,6 +834,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 6a25b7bb6..cf0a1e2a3 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: Namespace
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: Namespace
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index 02882c944..bf5361838 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_restricted_dashboard.golden b/cli/cmd/testdata/install_restricted_dashboard.golden
index f76d46478..326d1a089 100644
--- a/cli/cmd/testdata/install_restricted_dashboard.golden
+++ b/cli/cmd/testdata/install_restricted_dashboard.golden
@@ -772,6 +772,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/install_tracing.golden b/cli/cmd/testdata/install_tracing.golden
index e547b4981..6308b20ea 100644
--- a/cli/cmd/testdata/install_tracing.golden
+++ b/cli/cmd/testdata/install_tracing.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_default.golden b/cli/cmd/testdata/upgrade_default.golden
index 9de19b540..182558a11 100644
--- a/cli/cmd/testdata/upgrade_default.golden
+++ b/cli/cmd/testdata/upgrade_default.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_external_issuer.golden b/cli/cmd/testdata/upgrade_external_issuer.golden
index fca00a07a..9abe6dc15 100644
--- a/cli/cmd/testdata/upgrade_external_issuer.golden
+++ b/cli/cmd/testdata/upgrade_external_issuer.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_ha.golden b/cli/cmd/testdata/upgrade_ha.golden
index 872f7449b..f30911258 100644
--- a/cli/cmd/testdata/upgrade_ha.golden
+++ b/cli/cmd/testdata/upgrade_ha.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_ha_config.golden b/cli/cmd/testdata/upgrade_ha_config.golden
index 3519f6a10..f0b17d824 100644
--- a/cli/cmd/testdata/upgrade_ha_config.golden
+++ b/cli/cmd/testdata/upgrade_ha_config.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_overwrite_issuer.golden b/cli/cmd/testdata/upgrade_overwrite_issuer.golden
index ea34d289c..e0d1015e3 100644
--- a/cli/cmd/testdata/upgrade_overwrite_issuer.golden
+++ b/cli/cmd/testdata/upgrade_overwrite_issuer.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_overwrite_trust_anchors-external-issuer.golden b/cli/cmd/testdata/upgrade_overwrite_trust_anchors-external-issuer.golden
index 26bd483b1..7fdaa6d27 100644
--- a/cli/cmd/testdata/upgrade_overwrite_trust_anchors-external-issuer.golden
+++ b/cli/cmd/testdata/upgrade_overwrite_trust_anchors-external-issuer.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/cli/cmd/testdata/upgrade_overwrite_trust_anchors.golden b/cli/cmd/testdata/upgrade_overwrite_trust_anchors.golden
index ea34d289c..e0d1015e3 100644
--- a/cli/cmd/testdata/upgrade_overwrite_trust_anchors.golden
+++ b/cli/cmd/testdata/upgrade_overwrite_trust_anchors.golden
@@ -837,6 +837,9 @@ subjects:
 - kind: ServiceAccount
   name: linkerd-web
   namespace: linkerd
+- kind: ServiceAccount
+  name: linkerd-smi-metrics
+  namespace: linkerd
 ---
 ###
 ### SMI-Metrics RBAC
diff --git a/controller/api/destination/endpoint_translator_test.go b/controller/api/destination/endpoint_translator_test.go
index 3f5e22b38..9a07fe75c 100644
--- a/controller/api/destination/endpoint_translator_test.go
+++ b/controller/api/destination/endpoint_translator_test.go
@@ -105,6 +105,13 @@ var (
 		Port:     2,
 		Identity: "some-identity",
 	}
+
+	remoteGatewayWithTLSAndAuthOverride = watcher.Address{
+		IP:                "1.1.1.2",
+		Port:              2,
+		Identity:          "some-identity",
+		AuthorityOverride: "some-auth.com:2",
+	}
 )
 
 func makeEndpointTranslator(t *testing.T) (*mockDestinationGetServer, *endpointTranslator) {
@@ -165,6 +172,46 @@ func TestEndpointTranslatorForRemoteGateways(t *testing.T) {
 		}
 	})
 
+	t.Run("Sends TlsIdentity and Auth override when present", func(t *testing.T) {
+		expectedTLSIdentity := &pb.TlsIdentity_DnsLikeIdentity{
+			Name: "some-identity",
+		}
+
+		expectedProtocolHint := &pb.ProtocolHint{
+			Protocol: &pb.ProtocolHint_H2_{
+				H2: &pb.ProtocolHint_H2{},
+			},
+		}
+
+		expectedAuthOverride := &pb.AuthorityOverride{
+			AuthorityOverride: "some-auth.com:2",
+		}
+
+		mockGetServer, translator := makeEndpointTranslator(t)
+
+		translator.Add(mkAddressSetForServices(remoteGatewayWithTLSAndAuthOverride))
+
+		addrs := mockGetServer.updatesReceived[0].GetAdd().GetAddrs()
+		if len(addrs) != 1 {
+			t.Fatalf("Expected [1] address returned, got %v", addrs)
+		}
+
+		actualTLSIdentity := addrs[0].GetTlsIdentity().GetDnsLikeIdentity()
+		if !reflect.DeepEqual(actualTLSIdentity, expectedTLSIdentity) {
+			t.Fatalf("Expected TlsIdentity to be [%v] but was [%v]", expectedTLSIdentity, actualTLSIdentity)
+		}
+
+		actualProtocolHint := addrs[0].GetProtocolHint()
+		if !reflect.DeepEqual(actualProtocolHint, expectedProtocolHint) {
+			t.Fatalf("Expected ProtoclHint to be [%v] but was [%v]", expectedProtocolHint, actualProtocolHint)
+		}
+
+		actualAuthOverride := addrs[0].GetAuthorityOverride()
+		if !reflect.DeepEqual(actualProtocolHint, expectedProtocolHint) {
+			t.Fatalf("Expected AuthOverride to be [%v] but was [%v]", expectedAuthOverride, actualAuthOverride)
+		}
+	})
+
 	t.Run("Does not send TlsIdentity when not present", func(t *testing.T) {
 		mockGetServer, translator := makeEndpointTranslator(t)
 
diff --git a/controller/api/destination/watcher/endpoints_watcher_test.go b/controller/api/destination/watcher/endpoints_watcher_test.go
index 9e0e09b73..bd4178a89 100644
--- a/controller/api/destination/watcher/endpoints_watcher_test.go
+++ b/controller/api/destination/watcher/endpoints_watcher_test.go
@@ -31,6 +31,9 @@ func addressString(address Address) string {
 	if address.Identity != "" {
 		addressString = fmt.Sprintf("%s/%s", addressString, address.Identity)
 	}
+	if address.AuthorityOverride != "" {
+		addressString = fmt.Sprintf("%s/%s", addressString, address.AuthorityOverride)
+	}
 	return addressString
 }
 
@@ -664,7 +667,7 @@ subsets:
 			id:          ServiceID{Name: "name1-remote", Namespace: "ns"},
 			port:        8989,
 			expectedAddresses: []string{
-				"172.17.0.12:8989/gateway-identity-1",
+				"172.17.0.12:8989/gateway-identity-1/name1-remote-fq:8989",
 			},
 			expectedNoEndpoints:              false,
 			expectedNoEndpointsServiceExists: false,
@@ -700,7 +703,7 @@ subsets:
 			id:          ServiceID{Name: "name1-remote", Namespace: "ns"},
 			port:        8989,
 			expectedAddresses: []string{
-				"172.17.0.12:8989",
+				"172.17.0.12:8989/name1-remote-fq:8989",
 			},
 			expectedNoEndpoints:              false,
 			expectedNoEndpointsServiceExists: false,
@@ -738,7 +741,44 @@ subsets:
 			id:          ServiceID{Name: "name1-remote", Namespace: "ns"},
 			port:        8989,
 			expectedAddresses: []string{
-				"172.17.0.12:9999/gateway-identity-1",
+				"172.17.0.12:9999/gateway-identity-1/name1-remote-fq:8989",
+			},
+			expectedNoEndpoints:              false,
+			expectedNoEndpointsServiceExists: false,
+		},
+		{
+			k8sConfigs: []string{`
+apiVersion: v1
+kind: Service
+metadata:
+  name: name1-remote
+  namespace: ns
+spec:
+  type: LoadBalancer
+  ports:
+  - port: 8989`,
+				`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: name1-remote
+  namespace: ns
+  annotations:
+    mirror.linkerd.io/remote-gateway-identity: ""
+    mirror.linkerd.io/remote-svc-fq-name: "name1-remote-fq"
+  labels:
+    mirror.linkerd.io/mirrored-service: "true"
+subsets:
+- addresses:
+  - ip: 172.17.0.12
+  ports:
+  - port: 9999`,
+			},
+			serviceType: "mirrored service with empty identity and remapped port in endpoints",
+			id:          ServiceID{Name: "name1-remote", Namespace: "ns"},
+			port:        8989,
+			expectedAddresses: []string{
+				"172.17.0.12:9999/name1-remote-fq:8989",
 			},
 			expectedNoEndpoints:              false,
 			expectedNoEndpointsServiceExists: false,
diff --git a/controller/cmd/service-mirror/cluster_watcher_test.go b/controller/cmd/service-mirror/cluster_watcher_test.go
index eb8980aa1..e2189ed27 100644
--- a/controller/cmd/service-mirror/cluster_watcher_test.go
+++ b/controller/cmd/service-mirror/cluster_watcher_test.go
@@ -145,7 +145,7 @@ func TestRemoteServiceCreated(t *testing.T) {
 				mirroredService("service-one-remote", "ns1", "missing-gateway", "missing-namespace", "111", "", nil),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("service-one-remote", "ns1", "missing-gateway", "missing-namespace", "", nil),
+				endpoints("service-one-remote", "ns1", "missing-gateway", "missing-namespace", "", "", nil),
 			},
 		},
 		{
@@ -188,10 +188,10 @@ func TestRemoteServiceCreated(t *testing.T) {
 					}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("service-one-remote", "ns1", "existing-gateway", "existing-namespace", "", nil),
+				endpoints("service-one-remote", "ns1", "existing-gateway", "existing-namespace", "", "", nil),
 			},
 			remoteResources: []string{
-				gatewayAsYaml("existing-gateway", "existing-namespace", "222", "192.0.2.127", "incoming-port-wrong", 888, t),
+				gatewayAsYaml("existing-gateway", "existing-namespace", "222", "192.0.2.127", "incoming-port-wrong", 888, "", t),
 			},
 		},
 		{
@@ -239,7 +239,7 @@ func TestRemoteServiceCreated(t *testing.T) {
 					}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("service-one-remote", "ns1", "existing-gateway", "existing-namespace", "192.0.2.127", []corev1.EndpointPort{
+				endpoints("service-one-remote", "ns1", "existing-gateway", "existing-namespace", "192.0.2.127", "gateway-identity", []corev1.EndpointPort{
 					{
 						Name:     "port1",
 						Port:     888,
@@ -253,7 +253,7 @@ func TestRemoteServiceCreated(t *testing.T) {
 				}),
 			},
 			remoteResources: []string{
-				gatewayAsYaml("existing-gateway", "existing-namespace", "222", "192.0.2.127", "incoming-port", 888, t),
+				gatewayAsYaml("existing-gateway", "existing-namespace", "222", "192.0.2.127", "incoming-port", 888, "gateway-identity", t),
 			},
 		},
 	} {
@@ -275,7 +275,7 @@ func TestRemoteServiceDeleted(t *testing.T) {
 
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote-to-delete-remote", "test-namespace-to-delete", "", "", "", "", nil, t),
-				endpointsAsYaml("test-service-remote-to-delete-remote", "test-namespace-to-delete", "", "", "", nil, t),
+				endpointsAsYaml("test-service-remote-to-delete-remote", "test-namespace-to-delete", "", "", "", "gateway-identity", nil, t),
 			},
 		},
 	} {
@@ -314,7 +314,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 							Port:     222,
 						},
 					}),
-					localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", []corev1.EndpointPort{
+					localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", "", []corev1.EndpointPort{
 						{
 							Name:     "port1",
 							Port:     888,
@@ -333,7 +333,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 				},
 			},
 			remoteResources: []string{
-				gatewayAsYaml("gateway-new", "gateway-ns", "currentGatewayResVersion", "0.0.0.0", "incoming-port", 999, t),
+				gatewayAsYaml("gateway-new", "gateway-ns", "currentGatewayResVersion", "0.0.0.0", "incoming-port", 999, "", t),
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "past", "pastGatewayResVersion", []corev1.ServicePort{
@@ -348,7 +348,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 						Port:     222,
 					},
 				}, t),
-				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", []corev1.EndpointPort{
+				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", "", []corev1.EndpointPort{
 					{
 						Name:     "port1",
 						Port:     888,
@@ -383,7 +383,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 					}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-remote", "test-namespace", "gateway-new", "gateway-ns", "0.0.0.0", []corev1.EndpointPort{
+				endpoints("test-service-remote", "test-namespace", "gateway-new", "gateway-ns", "0.0.0.0", "", []corev1.EndpointPort{
 					{
 						Name:     "port1",
 						Port:     999,
@@ -425,7 +425,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 							Port:     222,
 						},
 					}),
-					localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", []corev1.EndpointPort{
+					localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", "", []corev1.EndpointPort{
 						{
 							Name:     "port1",
 							Port:     888,
@@ -444,7 +444,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 				},
 			},
 			remoteResources: []string{
-				gatewayAsYaml("gateway", "gateway-ns", "currentGatewayResVersion", "192.0.2.127", "incoming-port", 888, t),
+				gatewayAsYaml("gateway", "gateway-ns", "currentGatewayResVersion", "192.0.2.127", "incoming-port", 888, "", t),
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "past", "pastGatewayResVersion", []corev1.ServicePort{
@@ -464,7 +464,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 						Port:     333,
 					},
 				}, t),
-				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", []corev1.EndpointPort{
+				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", "", []corev1.EndpointPort{
 					{
 						Name:     "port1",
 						Port:     888,
@@ -499,7 +499,7 @@ func TestRemoteServiceUpdated(t *testing.T) {
 			},
 
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", []corev1.EndpointPort{
+				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "192.0.2.127", "", []corev1.EndpointPort{
 					{
 						Name:     "port1",
 						Port:     888,
@@ -572,14 +572,14 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 				}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-1-port",
 							Port:     999,
 							Protocol: "TCP",
 						}}),
-				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-2-port",
@@ -596,7 +596,7 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 							Port:     8081,
 						},
 					}, t),
-				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-1-port",
@@ -610,7 +610,7 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 						Port:     8082,
 					},
 				}, t),
-				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-2-port",
@@ -668,14 +668,14 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 				}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.1",
+				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.1", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-1-port",
 							Port:     888,
 							Protocol: "TCP",
 						}}),
-				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.1",
+				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.1", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-2-port",
@@ -693,7 +693,7 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 							Port:     8081,
 						},
 					}, t),
-				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-1-port",
@@ -707,7 +707,105 @@ func TestRemoteGatewayUpdated(t *testing.T) {
 						Port:     8082,
 					},
 				}, t),
-				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
+					[]corev1.EndpointPort{
+						{
+							Name:     "svc-2-port",
+							Port:     888,
+							Protocol: "TCP",
+						}}, t),
+			},
+		},
+
+		{
+			testDescription: "identity is updated on gateway change",
+			events: []interface{}{
+				&RemoteGatewayUpdated{
+					identity:             "new-identity",
+					newPort:              888,
+					newEndpointAddresses: []corev1.EndpointAddress{{IP: "0.0.0.0"}},
+					gatewayData: &gatewayMetadata{
+						Name:      "gateway",
+						Namespace: "gateway-ns",
+					},
+					newResourceVersion: "currentGatewayResVersion",
+					affectedServices: []*corev1.Service{
+						mirroredService("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "pastGatewayResVersion",
+							[]corev1.ServicePort{
+								{
+									Name:     "svc-1-port",
+									Protocol: "TCP",
+									Port:     8081,
+								},
+							}),
+						mirroredService("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "", "pastGatewayResVersion", []corev1.ServicePort{
+							{
+								Name:     "svc-2-port",
+								Protocol: "TCP",
+								Port:     8082,
+							},
+						}),
+					},
+				},
+			},
+			expectedLocalServices: []*corev1.Service{
+				mirroredService("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "currentGatewayResVersion",
+					[]corev1.ServicePort{
+						{
+							Name:     "svc-1-port",
+							Protocol: "TCP",
+							Port:     8081,
+						},
+					}),
+				mirroredService("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "", "currentGatewayResVersion", []corev1.ServicePort{
+					{
+						Name:     "svc-2-port",
+						Protocol: "TCP",
+						Port:     8082,
+					},
+				}),
+			},
+			expectedLocalEndpoints: []*corev1.Endpoints{
+				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "new-identity",
+					[]corev1.EndpointPort{
+						{
+							Name:     "svc-1-port",
+							Port:     888,
+							Protocol: "TCP",
+						}}),
+				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "new-identity",
+					[]corev1.EndpointPort{
+						{
+							Name:     "svc-2-port",
+							Port:     888,
+							Protocol: "TCP",
+						}}),
+			},
+
+			localResources: []string{
+				mirroredServiceAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "pastGatewayResVersion",
+					[]corev1.ServicePort{
+						{
+							Name:     "svc-1-port",
+							Protocol: "TCP",
+							Port:     8081,
+						},
+					}, t),
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
+					[]corev1.EndpointPort{
+						{
+							Name:     "svc-1-port",
+							Port:     888,
+							Protocol: "TCP",
+						}}, t),
+				mirroredServiceAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "", "pastGatewayResVersion", []corev1.ServicePort{
+					{
+						Name:     "svc-2-port",
+						Protocol: "TCP",
+						Port:     8082,
+					},
+				}, t),
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-2-port",
@@ -752,8 +850,8 @@ func TestRemoteGatewayDeleted(t *testing.T) {
 				}),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", nil),
-				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "", nil),
+				endpoints("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "", nil),
+				endpoints("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "", "", nil),
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "pastGatewayResVersion",
@@ -764,7 +862,7 @@ func TestRemoteGatewayDeleted(t *testing.T) {
 							Port:     8081,
 						},
 					}, t),
-				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-1-port",
@@ -778,7 +876,7 @@ func TestRemoteGatewayDeleted(t *testing.T) {
 						Port:     8082,
 					},
 				}, t),
-				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0",
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "",
 					[]corev1.EndpointPort{
 						{
 							Name:     "svc-2-port",
@@ -802,9 +900,9 @@ func TestClusterUnregistered(t *testing.T) {
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-1-remote", "test-namespace", "", "", "", "", nil, t),
-				endpointsAsYaml("test-service-1-remote", "test-namespace", "", "", "", nil, t),
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "", "", "", "", nil, t),
 				mirroredServiceAsYaml("test-service-2-remote", "test-namespace", "", "", "", "", nil, t),
-				endpointsAsYaml("test-service-2-remote", "test-namespace", "", "", "", nil, t),
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "", "", "", "", nil, t),
 			},
 		},
 	} {
@@ -822,9 +920,9 @@ func TestGcOrphanedServices(t *testing.T) {
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-1-remote", "test-namespace", "gateway", "gateway-ns", "", "", nil, t),
-				endpointsAsYaml("test-service-1-remote", "test-namespace", "", "", "", nil, t),
+				endpointsAsYaml("test-service-1-remote", "test-namespace", "", "", "", "", nil, t),
 				mirroredServiceAsYaml("test-service-2-remote", "test-namespace", "", "", "", "", nil, t),
-				endpointsAsYaml("test-service-2-remote", "test-namespace", "", "", "", nil, t),
+				endpointsAsYaml("test-service-2-remote", "test-namespace", "", "", "", "", nil, t),
 			},
 			remoteResources: []string{
 				remoteServiceAsYaml("test-service-1", "test-namespace", "gateway", "gateway-ns", "", nil, t),
@@ -835,7 +933,7 @@ func TestGcOrphanedServices(t *testing.T) {
 			},
 
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-1-remote", "test-namespace", "", "", "", nil),
+				endpoints("test-service-1-remote", "test-namespace", "", "", "", "", nil),
 			},
 		},
 	} {
@@ -891,11 +989,11 @@ func onAddOrUpdateTestCases(t *testing.T, isAdd bool) []testCase {
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "pastResourceVersion", "gatewayResVersion", nil, t),
-				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil, t),
+				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil, t),
 			},
 			expectedEventsInQueue: []interface{}{&RemoteServiceUpdated{
 				localService:   mirroredService("test-service-remote", "test-namespace", "gateway", "gateway-ns", "pastResourceVersion", "gatewayResVersion", nil),
-				localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil),
+				localEndpoints: endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil),
 				remoteUpdate:   remoteService("test-service", "test-namespace", "gateway", "gateway-ns", "currentResVersion", nil),
 				gatewayData: &gatewayMetadata{
 					Name:      "gateway",
@@ -906,23 +1004,23 @@ func onAddOrUpdateTestCases(t *testing.T, isAdd bool) []testCase {
 				mirroredService("test-service-remote", "test-namespace", "gateway", "gateway-ns", "pastResourceVersion", "gatewayResVersion", nil),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil),
+				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil),
 			},
 		},
 		{
-			testDescription: fmt.Sprintf("not not enqueue any events as this update does not really tell us anything new (res version is the same...) (%s)", testType),
+			testDescription: fmt.Sprintf("not enqueue any events as this update does not really tell us anything new (res version is the same...) (%s)", testType),
 			events: []interface{}{
 				onAddOrUpdateEvent(isAdd, remoteService("test-service", "test-namespace", "gateway", "gateway-ns", "currentResVersion", nil)),
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "currentResVersion", "gatewayResVersion", nil, t),
-				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil, t),
+				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil, t),
 			},
 			expectedLocalServices: []*corev1.Service{
 				mirroredService("test-service-remote", "test-namespace", "gateway", "gateway-ns", "currentResVersion", "gatewayResVersion", nil),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil),
+				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil),
 			},
 		},
 		{
@@ -932,7 +1030,7 @@ func onAddOrUpdateTestCases(t *testing.T, isAdd bool) []testCase {
 			},
 			localResources: []string{
 				mirroredServiceAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "currentResVersion", "gatewayResVersion", nil, t),
-				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil, t),
+				endpointsAsYaml("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil, t),
 			},
 			expectedEventsInQueue: []interface{}{&RemoteServiceDeleted{
 				Name:      "test-service",
@@ -943,7 +1041,7 @@ func onAddOrUpdateTestCases(t *testing.T, isAdd bool) []testCase {
 				mirroredService("test-service-remote", "test-namespace", "gateway", "gateway-ns", "currentResVersion", "gatewayResVersion", nil),
 			},
 			expectedLocalEndpoints: []*corev1.Endpoints{
-				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", nil),
+				endpoints("test-service-remote", "test-namespace", "gateway", "gateway-ns", "0.0.0.0", "", nil),
 			},
 		},
 	}
diff --git a/controller/cmd/service-mirror/cluster_watcher_test_util.go b/controller/cmd/service-mirror/cluster_watcher_test_util.go
index 05d73a4a5..17869dbcb 100644
--- a/controller/cmd/service-mirror/cluster_watcher_test_util.go
+++ b/controller/cmd/service-mirror/cluster_watcher_test_util.go
@@ -131,7 +131,7 @@ func mirroredServiceAsYaml(name, namespace, gtwName, gtwNs, resourceVersion, gat
 	return string(bytes)
 }
 
-func gateway(name, namespace, resourceVersion, ip, portName string, port int32) *corev1.Service {
+func gateway(name, namespace, resourceVersion, ip, portName string, port int32, identity string) *corev1.Service {
 	svc := corev1.Service{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Service",
@@ -141,6 +141,9 @@ func gateway(name, namespace, resourceVersion, ip, portName string, port int32)
 			Name:            name,
 			Namespace:       namespace,
 			ResourceVersion: resourceVersion,
+			Annotations: map[string]string{
+				consts.GatewayIdentity: identity,
+			},
 		},
 		Spec: corev1.ServiceSpec{
 			Ports: []corev1.ServicePort{
@@ -159,8 +162,8 @@ func gateway(name, namespace, resourceVersion, ip, portName string, port int32)
 	return &svc
 }
 
-func gatewayAsYaml(name, namespace, resourceVersion, ip, portName string, port int32, t *testing.T) string {
-	gtw := gateway(name, namespace, resourceVersion, ip, portName, port)
+func gatewayAsYaml(name, namespace, resourceVersion, ip, portName string, port int32, identity string, t *testing.T) string {
+	gtw := gateway(name, namespace, resourceVersion, ip, portName, port, identity)
 
 	bytes, err := yaml.Marshal(gtw)
 	if err != nil {
@@ -169,7 +172,7 @@ func gatewayAsYaml(name, namespace, resourceVersion, ip, portName string, port i
 	return string(bytes)
 }
 
-func endpoints(name, namespace, gtwName, gtwNs, gatewayIP string, ports []corev1.EndpointPort) *corev1.Endpoints {
+func endpoints(name, namespace, gtwName, gtwNs, gatewayIP string, gatewayIdentity string, ports []corev1.EndpointPort) *corev1.Endpoints {
 	var subsets []corev1.EndpointSubset
 	if gatewayIP != "" {
 		subsets = []corev1.EndpointSubset{
@@ -184,7 +187,7 @@ func endpoints(name, namespace, gtwName, gtwNs, gatewayIP string, ports []corev1
 		}
 	}
 
-	return &corev1.Endpoints{
+	endpoints := &corev1.Endpoints{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Endpoints",
 			APIVersion: "v1",
@@ -198,13 +201,22 @@ func endpoints(name, namespace, gtwName, gtwNs, gatewayIP string, ports []corev1
 				consts.RemoteGatewayNameLabel: gtwName,
 				consts.RemoteGatewayNsLabel:   gtwNs,
 			},
+			Annotations: map[string]string{
+				consts.RemoteServiceFqName: fmt.Sprintf("%s.%s.svc.cluster.local", strings.Replace(name, "-remote", "", 1), namespace),
+			},
 		},
 		Subsets: subsets,
 	}
+
+	if gatewayIdentity != "" {
+		endpoints.Annotations[consts.RemoteGatewayIdentity] = gatewayIdentity
+	}
+
+	return endpoints
 }
 
-func endpointsAsYaml(name, namespace, gtwName, gtwNs, gatewayIP string, ports []corev1.EndpointPort, t *testing.T) string {
-	ep := endpoints(name, namespace, gtwName, gtwNs, gatewayIP, ports)
+func endpointsAsYaml(name, namespace, gtwName, gtwNs, gatewayIP, gatewayIdentity string, ports []corev1.EndpointPort, t *testing.T) string {
+	ep := endpoints(name, namespace, gtwName, gtwNs, gatewayIP, gatewayIdentity, ports)
 
 	bytes, err := yaml.Marshal(ep)
 	if err != nil {
diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go
index d0f6b54a9..9c45c1e1e 100644
--- a/pkg/healthcheck/healthcheck_test.go
+++ b/pkg/healthcheck/healthcheck_test.go
@@ -749,6 +749,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-destination
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-identity
   namespace: test-ns
@@ -776,6 +785,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-smi-metrics
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-sp-validator
   namespace: test-ns
@@ -942,6 +960,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-destination
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-identity
   namespace: test-ns
@@ -969,6 +996,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-smi-metrics
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-sp-validator
   namespace: test-ns
@@ -1144,6 +1180,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-destination
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-identity
   namespace: test-ns
@@ -1171,6 +1216,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-smi-metrics
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-sp-validator
   namespace: test-ns
@@ -1355,6 +1409,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-destination
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-identity
   namespace: test-ns
@@ -1382,6 +1445,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-smi-metrics
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-sp-validator
   namespace: test-ns
@@ -1575,6 +1647,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-destination
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-identity
   namespace: test-ns
@@ -1602,6 +1683,15 @@ metadata:
 				`
 kind: ServiceAccount
 apiVersion: v1
+metadata:
+  name: linkerd-smi-metrics
+  namespace: test-ns
+  labels:
+    linkerd.io/control-plane-ns: test-ns
+`,
+				`
+kind: ServiceAccount
+apiVersion: v1
 metadata:
   name: linkerd-sp-validator
   namespace: test-ns
diff --git a/test/install_test.go b/test/install_test.go
index a4fac2037..92dd6024a 100644
--- a/test/install_test.go
+++ b/test/install_test.go
@@ -416,6 +416,24 @@ func TestCheckHelmStableBeforeUpgrade(t *testing.T) {
 	if TestHelper.UpgradeHelmFromVersion() == "" {
 		t.Skip("Skipping as this is not a helm upgrade test")
 	}
+
+	// TODO: remove when 2.8.0 is released
+	_, err := TestHelper.Kubectl("",
+		"--namespace", TestHelper.GetLinkerdNamespace(),
+		"create", "serviceaccount", "linkerd-smi-metrics",
+	)
+	if err != nil {
+		t.Fatalf("linkerd-smi-metrics SA creation failed: %s", err)
+	}
+	_, err = TestHelper.Kubectl("",
+		"--namespace", TestHelper.GetLinkerdNamespace(),
+		"label", "serviceaccount", "linkerd-smi-metrics",
+		"linkerd.io/control-plane-ns="+TestHelper.GetLinkerdNamespace(),
+	)
+	if err != nil {
+		t.Fatalf("linkerd-smi-metrics SA labeling failed: %s", err)
+	}
+
 	testCheckCommand(t, "", TestHelper.UpgradeHelmFromVersion(), "", TestHelper.UpgradeHelmFromVersion())
 }
 
@@ -424,6 +442,16 @@ func TestUpgradeHelm(t *testing.T) {
 		t.Skip("Skipping as this is not a helm upgrade test")
 	}
 
+	// TODO: remove when 2.8.0 is released
+	_, err := TestHelper.Kubectl("",
+		"--namespace", TestHelper.GetLinkerdNamespace(),
+		"delete", "serviceaccount", "linkerd-smi-metrics",
+	)
+	if err != nil {
+		t.Fatalf("linkerd-smi-metrics SA deletion failed: %s", err)
+	}
+	time.Sleep(3 * time.Second)
+
 	args := []string{
 		"--reset-values",
 		"--atomic",