Repository: linkerd/linkerd2 (go)

Issue:
check: pull trust anchor certificate from `linkerd-identity-trust-roots`
### Is there an existing issue for this?

- [X] I have searched the existing issues

### What is the issue?

We introduced `linkerd-identity-trust-roots` as a configmap to help people have more control when it comes to managing trust anchors for their linkerd installation.

It was pointed out that when installing linkerd with `externalCA` and `externalIssuer` options, the check will fail when matching the trust bundle the proxies have with what's stored in linkerd's config. This is also the case when using extensions that have their own checks, e.g `viz`.

```
Cannot connect to Linkerd Viz: Some pods do not have the current trust bundle and must be restarted:
        * grafana-6fbc6dbf86-mz282
        * metrics-api-545b8d457b-jzdq7
        * prometheus-5d747596d-hfdlb
        * tap-85f7b8b69c-z6jxn
        * tap-injector-6fc7ddd46c-68pc6
        * web-75447677d7-cdl2z
Validate the install with: linkerd viz check
```

I think the problem comes from the fact that during installation, linkerd will store the trust root in both `linkerd-config` and `linkerd-identity-trust-roots`. When doing the check, the healthchecker will use the trust from `linkerd-config`. If linkerd is  installed without manually passing in the certificates, then there will be a mismatch.


### How can it be reproduced?

Install linkerd with a CA managed by cert-manager, or with an existing `linkerd-identity-trust-roots`.

### Logs, error output, etc

× viz extension proxies are healthy
    Some pods do not have the current trust bundle and must be restarted:
        * grafana-6fbc6dbf86-mz282
        * metrics-api-545b8d457b-jzdq7
        * prometheus-5d747596d-hfdlb
        * tap-85f7b8b69c-z6jxn
        * tap-injector-6fc7ddd46c-68pc6
        * web-75447677d7-cdl2z
    see https://linkerd.io/2.11/checks/#l5d-viz-proxy-healthy for hints

### output of `linkerd check -o short`

```text
N/A
```

### Environment

N/A

### Possible solution

In the healthchecker, fetch the certificates from the trust-root configmap.

### Additional context

_No response_

### Would you like to work on fixing this bug?

_No response_

Pull request:
check: pull trust anchor certificate from linkerd-identity-trust-roots
Fixes #7449 

This PR updates the health checker to use the trust root from the config map `linkerd-identity-trust-roots`. This will prevent a certificate mismatch, when linkerd is installed without manually passing in the certificates.

Signed-off-by: Wim de Groot <34519486+degrootwim@users.noreply.github.com>


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index e8659ab57..5bd4a153a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -895,9 +895,9 @@ dependencies = [
 
 [[package]]
 name = "linkerd2-proxy-api"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d20d7fd6e5b0f0af5cecca5deded293266ae5cfb3aff31ef632da48640ca522"
+checksum = "c6aaf91178c272abeaac52b2472351e73affa723bfdd0c15e147e2f975f2fbe5"
 dependencies = [
  "ipnet",
  "prost",
diff --git a/go.mod b/go.mod
index 8c8bf96c2..0ede718fd 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
 	github.com/imdario/mergo v0.3.12
 	github.com/julienschmidt/httprouter v1.3.0
-	github.com/linkerd/linkerd2-proxy-api v0.3.0
+	github.com/linkerd/linkerd2-proxy-api v0.3.1
 	github.com/linkerd/linkerd2-proxy-init v1.5.2
 	github.com/mattn/go-isatty v0.0.14
 	github.com/mattn/go-runewidth v0.0.13
diff --git a/go.sum b/go.sum
index fdb9f9605..27c646c0c 100644
--- a/go.sum
+++ b/go.sum
@@ -694,8 +694,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
-github.com/linkerd/linkerd2-proxy-api v0.3.0 h1:TUfIw+XffDQQ3SQEJcIiHnZgZZxKK/ZfsVx7SeZH2nM=
-github.com/linkerd/linkerd2-proxy-api v0.3.0/go.mod h1:BuA+jXhgCXiBJkNjQrA9h1PLsJN5z6NOYJI5KVYDc2U=
+github.com/linkerd/linkerd2-proxy-api v0.3.1 h1:OkoHjkkKxSWy+HyeCCFD6MHvOBGQ2ZfxV6ygfm0XuuQ=
+github.com/linkerd/linkerd2-proxy-api v0.3.1/go.mod h1:n4CuuDqQXGAbFnxA22OD3NXkLyEayljWNhMVr0qgi9o=
 github.com/linkerd/linkerd2-proxy-init v1.5.2 h1:haasRTIzDGbjbMRhY0RwQRbWn1b1VqymGwiy38xwpzY=
 github.com/linkerd/linkerd2-proxy-init v1.5.2/go.mod h1:aBd7DEKESAVQR+9rZ1Oz9wgGFaCME1F9Dm3XCD1pqUY=
 github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
@@ -1586,7 +1586,6 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
 google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
 google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
 google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index 4a59e956f..f342c62dc 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -1773,6 +1773,13 @@ func (hc *HealthChecker) fetchWebhookCaBundle(ctx context.Context, webhook strin
 	return caBundle, nil
 }
 
+// FetchTrustBundle retrieves the ca-bundle from the config-map linkerd-identity-trust-roots
+func FetchTrustBundle(ctx context.Context, kubeAPI k8s.KubernetesAPI, controlPlaneNamespace string) (string, error) {
+	configMap, err := kubeAPI.CoreV1().ConfigMaps(controlPlaneNamespace).Get(ctx, "linkerd-identity-trust-roots", metav1.GetOptions{})
+
+	return configMap.Data["ca-bundle.crt"], err
+}
+
 // FetchCredsFromSecret retrieves the TLS creds given a secret name
 func (hc *HealthChecker) FetchCredsFromSecret(ctx context.Context, namespace string, secretName string) (*tls.Cred, error) {
 	secret, err := hc.kubeAPI.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{})
@@ -2169,12 +2176,11 @@ func checkPodsProxiesCertificate(ctx context.Context, kubeAPI k8s.KubernetesAPI,
 		return err
 	}
 
-	_, values, err := FetchCurrentConfiguration(ctx, kubeAPI, controlPlaneNamespace)
+	trustAnchorsPem, err := FetchTrustBundle(ctx, kubeAPI, controlPlaneNamespace)
 	if err != nil {
 		return err
 	}
 
-	trustAnchorsPem := values.IdentityTrustAnchorsPEM
 	offendingPods := []string{}
 	for _, pod := range meshedPods {
 		// Skip control plane pods since they load their trust anchors from the linkerd-identity-trust-anchors configmap.


Test patch:
diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go
index 11fe096f8..7ec99bd25 100644
--- a/pkg/healthcheck/healthcheck_test.go
+++ b/pkg/healthcheck/healthcheck_test.go
@@ -1276,16 +1276,15 @@ func TestCheckDataPlaneProxiesCertificate(t *testing.T) {
 	const currentCertificate = "current-certificate"
 	const oldCertificate = "old-certificate"
 
-	linkerdConfigMap := fmt.Sprintf(`
+	linkerdIdentityTrustRoots := fmt.Sprintf(`
 kind: ConfigMap
 apiVersion: v1
 metadata:
   name: %s
 data:
-  values: |
-    identityTrustAnchorsPEM: %s
+  ca-bundle.crt: %s
 
-`, k8s.ConfigConfigMapName, currentCertificate)
+`, "linkerd-identity-trust-roots", currentCertificate)
 
 	var testCases = []struct {
 		checkDescription string
@@ -1338,7 +1337,7 @@ data:
 			hc.DataPlaneNamespace = testCase.namespace
 
 			var err error
-			hc.kubeAPI, err = k8s.NewFakeAPI(append(testCase.resources, linkerdConfigMap)...)
+			hc.kubeAPI, err = k8s.NewFakeAPI(append(testCase.resources, linkerdIdentityTrustRoots)...)
 			if err != nil {
 				t.Fatalf("Unexpected error: %q", err)
 			}