Repository: linkerd/linkerd2
Pull request #4121 (2020-02-28 20:41:36)

Add Commonbond to adopters
<!--  Thanks for sending a pull request!

If you already have a well-structured git commit message, chances are GitHub
set the title and description of this PR to the git commit message subject and
body, respectively. If so, you may delete these instructions and submit your PR.

If this is your first time, please read our contributor guide:
https://github.com/linkerd/linkerd2/blob/master/CONTRIBUTING.md

The title and description of your Pull Request should match the git commit
subject and body, respectively. Git commit messages are structured as follows:

```
Subject

Problem

Solution

Validation

Fixes #[GitHub issue ID]

DCO Sign off
```

Example git commit message:

```
Introduce Pull Request Template

GitHub's community guidelines recommend a pull request template, the repo was
lacking one.

Introduce a `PULL_REQUEST_TEMPLATE.md` file.

Once merged, the
[Community profile checklist](https://github.com/linkerd/linkerd2/community)
should indicate the repo now provides a pull request template.

Fixes #3321

Signed-off-by: Jane Smith <jane.smith@example.com>
```

Note the git commit message subject becomes the pull request title.

For more details around git commits, see the section on Committing in our
contributor guide:
https://github.com/linkerd/linkerd2/blob/master/CONTRIBUTING.md#committing
-->



Patch:
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 82ac411ce..0695a9104 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -6,6 +6,7 @@
 - [Attest](https://www.askattest.com)
 - [Buoyant](https://buoyant.io)
 - [Candide](https://candidegardening.com)
+- [Commonbond](https://www.commonbond.co/)
 - [Expedia](https://www.expedia.com)
 - [Facelift](https://www.facelift-bbt.com/en)
 - [finleap connect](https://connect.finleap.com/)
diff --git a/controller/tap/apiserver.go b/controller/tap/apiserver.go
index 734e14877..789c2e791 100644
--- a/controller/tap/apiserver.go
+++ b/controller/tap/apiserver.go
@@ -11,6 +11,7 @@ import (
 	"github.com/julienschmidt/httprouter"
 	"github.com/linkerd/linkerd2/controller/gen/controller/tap"
 	"github.com/linkerd/linkerd2/controller/k8s"
+	k8sutils "github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/prometheus"
 	"github.com/sirupsen/logrus"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -122,15 +123,17 @@ func (a *apiServer) validate(req *http.Request) error {
 // kubectl -n kube-system get cm/extension-apiserver-authentication
 // accessible via the extension-apiserver-authentication-reader role
 func apiServerAuth(k8sAPI *k8s.API) (string, []string, string, string, error) {
-	cmName := "extension-apiserver-authentication"
 
 	cm, err := k8sAPI.Client.CoreV1().
-		ConfigMaps("kube-system").
-		Get(cmName, metav1.GetOptions{})
+		ConfigMaps(metav1.NamespaceSystem).
+		Get(k8sutils.ExtensionAPIServerAuthenticationConfigMapName, metav1.GetOptions{})
+
 	if err != nil {
-		return "", nil, "", "", fmt.Errorf("failed to load [%s] config: %s", cmName, err)
+		return "", nil, "", "", fmt.Errorf("failed to load [%s] config: %s", k8sutils.ExtensionAPIServerAuthenticationConfigMapName, err)
 	}
-	clientCAPem, ok := cm.Data["requestheader-client-ca-file"]
+
+	clientCAPem, ok := cm.Data[k8sutils.ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey]
+
 	if !ok {
 		return "", nil, "", "", fmt.Errorf("no client CA cert available for apiextension-server")
 	}
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index d23906714..f8caca5bb 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -489,6 +489,13 @@ func (hc *HealthChecker) allCategories() []category {
 						return hc.checkCanGet(hc.ControlPlaneNamespace, "", "v1", "secrets")
 					},
 				},
+				{
+					description: "can read extension-apiserver-authentication configmap",
+					hintAnchor:  "pre-k8s",
+					check: func(context.Context) error {
+						return hc.checkExtensionAPIServerAuthentication()
+					},
+				},
 				{
 					description: "no clock skew detected",
 					hintAnchor:  "pre-k8s-clock-skew",
@@ -1907,7 +1914,19 @@ func (hc *HealthChecker) checkCapability(cap string) error {
 
 	return fmt.Errorf("found %d PodSecurityPolicies, but none provide %s, proxy injection will fail if the PSP admission controller is running", len(pspList.Items), cap)
 }
-
+func (hc *HealthChecker) checkExtensionAPIServerAuthentication() error {
+	if hc.kubeAPI == nil {
+		return fmt.Errorf("unexpected error: Kubernetes ClientSet not initialized")
+	}
+	m, err := hc.kubeAPI.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(k8s.ExtensionAPIServerAuthenticationConfigMapName, metav1.GetOptions{})
+	if err != nil {
+		return err
+	}
+	if v, exists := m.Data[k8s.ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey]; !exists || v == "" {
+		return fmt.Errorf("--%s is not configured", k8s.ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey)
+	}
+	return nil
+}
 func (hc *HealthChecker) checkClockSkew() error {
 	if hc.kubeAPI == nil {
 		// we should never get here
diff --git a/pkg/k8s/labels.go b/pkg/k8s/labels.go
index cf4a2fca4..2b8e43e70 100644
--- a/pkg/k8s/labels.go
+++ b/pkg/k8s/labels.go
@@ -30,6 +30,14 @@ const (
 	// control plane (e.g. web, controller).
 	ControllerComponentLabel = Prefix + "/control-plane-component"
 
+	// ExtensionAPIServerAuthenticationConfigMapName is the name of the ConfigMap where
+	// authentication data for extension API servers is placed.
+	ExtensionAPIServerAuthenticationConfigMapName = "extension-apiserver-authentication"
+
+	// ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey is the key that
+	// contains the value of the "--requestheader-client-ca-file" flag.
+	ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey = "requestheader-client-ca-file"
+
 	// ControllerNSLabel is injected into mesh-enabled apps, identifying the
 	// namespace of the Linkerd control plane.
 	ControllerNSLabel = Prefix + "/control-plane-ns"


Test patch:
diff --git a/controller/tap/apiserver_test.go b/controller/tap/apiserver_test.go
index 6c0c332f1..61a4f25ca 100644
--- a/controller/tap/apiserver_test.go
+++ b/controller/tap/apiserver_test.go
@@ -4,7 +4,6 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509/pkix"
-	"errors"
 	"fmt"
 	"net"
 	"net/http"
@@ -13,6 +12,7 @@ import (
 	"testing"
 
 	"github.com/linkerd/linkerd2/controller/k8s"
+	k8sutils "github.com/linkerd/linkerd2/pkg/k8s"
 )
 
 func TestNewAPIServer(t *testing.T) {
@@ -21,7 +21,7 @@ func TestNewAPIServer(t *testing.T) {
 		err    error
 	}{
 		{
-			err: errors.New("failed to load [extension-apiserver-authentication] config: configmaps \"extension-apiserver-authentication\" not found"),
+			err: fmt.Errorf("failed to load [%s] config: configmaps %q not found", k8sutils.ExtensionAPIServerAuthenticationConfigMapName, k8sutils.ExtensionAPIServerAuthenticationConfigMapName),
 		},
 		{
 			err: nil,
@@ -72,7 +72,7 @@ func TestAPIServerAuth(t *testing.T) {
 		err            error
 	}{
 		{
-			err: errors.New("failed to load [extension-apiserver-authentication] config: configmaps \"extension-apiserver-authentication\" not found"),
+			err: fmt.Errorf("failed to load [%s] config: configmaps \"%s\" not found", k8sutils.ExtensionAPIServerAuthenticationConfigMapName, k8sutils.ExtensionAPIServerAuthenticationConfigMapName),
 		},
 		{
 			k8sRes: []string{`
diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go
index 777ea8faa..52764d7c2 100644
--- a/pkg/healthcheck/healthcheck_test.go
+++ b/pkg/healthcheck/healthcheck_test.go
@@ -394,6 +394,63 @@ func TestCheckCanCreate(t *testing.T) {
 	}
 }
 
+func TestCheckExtensionAPIServerAuthentication(t *testing.T) {
+	tests := []struct {
+		k8sConfigs []string
+		err        error
+	}{
+		{
+			[]string{},
+			fmt.Errorf("configmaps %q not found", k8s.ExtensionAPIServerAuthenticationConfigMapName),
+		},
+		{
+			[]string{`
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: extension-apiserver-authentication
+ namespace: kube-system
+data:
+ foo : 'bar'
+ `,
+			},
+			fmt.Errorf("--%s is not configured", k8s.ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey),
+		},
+		{
+
+			[]string{fmt.Sprintf(`
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: extension-apiserver-authentication 
+ namespace: kube-system
+data:
+  %s : 'bar'
+  `, k8s.ExtensionAPIServerAuthenticationRequestHeaderClientCAFileKey)},
+			nil,
+		},
+	}
+	for i, test := range tests {
+		test := test
+		t.Run(fmt.Sprintf("%d: returns expected extension apiserver authentication check result", i), func(t *testing.T) {
+			hc := NewHealthChecker([]CategoryID{}, &Options{})
+			var err error
+			hc.kubeAPI, err = k8s.NewFakeAPI(test.k8sConfigs...)
+			if err != nil {
+				t.Fatal(err)
+			}
+			err = hc.checkExtensionAPIServerAuthentication()
+			if err != nil || test.err != nil {
+				if (err == nil && test.err != nil) ||
+					(err != nil && test.err == nil) ||
+					(err.Error() != test.err.Error()) {
+					t.Fatalf("Unexpected error (Expected: %s, Got: %s)", test.err, err)
+				}
+			}
+		})
+	}
+}
+
 func TestCheckClockSkew(t *testing.T) {
 	tests := []struct {
 		k8sConfigs []string
diff --git a/test/testdata/check.pre.golden b/test/testdata/check.pre.golden
index adf35a53b..1918d3002 100644
--- a/test/testdata/check.pre.golden
+++ b/test/testdata/check.pre.golden
@@ -19,6 +19,7 @@ pre-kubernetes-setup
 √ can create ConfigMaps
 √ can create Secrets
 √ can read Secrets
+√ can read extension-apiserver-authentication configmap
 √ no clock skew detected
 
 pre-kubernetes-capability