...

Source file src/github.com/linkerd/linkerd2/viz/tap/api/server_test.go

Documentation: github.com/linkerd/linkerd2/viz/tap/api

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"crypto/x509/pkix"
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"testing"
    13  
    14  	"github.com/go-test/deep"
    15  	"github.com/linkerd/linkerd2/controller/k8s"
    16  	k8sutils "github.com/linkerd/linkerd2/pkg/k8s"
    17  )
    18  
    19  func TestAPIServerAuth(t *testing.T) {
    20  	expectations := []struct {
    21  		k8sRes         []string
    22  		clientCAPem    string
    23  		allowedNames   []string
    24  		usernameHeader string
    25  		groupHeader    string
    26  		err            error
    27  	}{
    28  		{
    29  			err: fmt.Errorf("failed to load [%s] config: configmaps %q not found", k8sutils.ExtensionAPIServerAuthenticationConfigMapName, k8sutils.ExtensionAPIServerAuthenticationConfigMapName),
    30  		},
    31  		{
    32  			k8sRes: []string{`
    33  apiVersion: v1
    34  kind: ConfigMap
    35  metadata:
    36    name: extension-apiserver-authentication
    37    namespace: kube-system
    38  data:
    39    client-ca-file: 'client-ca-file'
    40    requestheader-allowed-names: '["name1", "name2"]'
    41    requestheader-client-ca-file: 'requestheader-client-ca-file'
    42    requestheader-extra-headers-prefix: '["X-Remote-Extra-"]'
    43    requestheader-group-headers: '["X-Remote-Group"]'
    44    requestheader-username-headers: '["X-Remote-User"]'
    45  `,
    46  			},
    47  			clientCAPem:    "requestheader-client-ca-file",
    48  			allowedNames:   []string{"name1", "name2"},
    49  			usernameHeader: "X-Remote-User",
    50  			groupHeader:    "X-Remote-Group",
    51  			err:            nil,
    52  		},
    53  	}
    54  
    55  	ctx := context.Background()
    56  	for i, exp := range expectations {
    57  		exp := exp // pin
    58  
    59  		t.Run(fmt.Sprintf("%d parses the apiServerAuth ConfigMap", i), func(t *testing.T) {
    60  			k8sAPI, err := k8s.NewFakeAPI(exp.k8sRes...)
    61  			if err != nil {
    62  				t.Fatalf("NewFakeAPI returned an error: %s", err)
    63  			}
    64  
    65  			clientCAPem, allowedNames, usernameHeader, groupHeader, err := serverAuth(ctx, k8sAPI)
    66  
    67  			if err != nil && exp.err != nil {
    68  				if err.Error() != exp.err.Error() {
    69  					t.Errorf("apiServerAuth returned unexpected error: %q, expected: %q", err, exp.err)
    70  				}
    71  			} else if err != nil {
    72  				t.Fatalf("Unexpected error: %s", err)
    73  			} else if exp.err != nil {
    74  				t.Fatalf("Did not encounter expected error: %s", err)
    75  			}
    76  
    77  			if clientCAPem != exp.clientCAPem {
    78  				t.Errorf("apiServerAuth returned unexpected clientCAPem: %q, expected: %q", clientCAPem, exp.clientCAPem)
    79  			}
    80  			if diff := deep.Equal(allowedNames, exp.allowedNames); diff != nil {
    81  				t.Errorf("%v", diff)
    82  			}
    83  			if usernameHeader != exp.usernameHeader {
    84  				t.Errorf("apiServerAuth returned unexpected usernameHeader: %q, expected: %q", usernameHeader, exp.usernameHeader)
    85  			}
    86  			if groupHeader != exp.groupHeader {
    87  				t.Errorf("apiServerAuth returned unexpected groupHeader: %q, expected: %q", groupHeader, exp.groupHeader)
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func TestValidate(t *testing.T) {
    94  	cert := testCertificate()
    95  	cert.Subject.CommonName = "name-any"
    96  
    97  	tls := tls.ConnectionState{PeerCertificates: []*x509.Certificate{&cert}}
    98  
    99  	req := http.Request{TLS: &tls}
   100  
   101  	server := Server{}
   102  	if err := server.validate(&req); err != nil {
   103  		t.Fatalf("No error expected for %q but encountered %q", cert.Subject.CommonName, err.Error())
   104  	}
   105  }
   106  
   107  func TestValidate_ClientAllowed(t *testing.T) {
   108  	cert := testCertificate()
   109  	cert.Subject.CommonName = "name-trusted"
   110  
   111  	tls := tls.ConnectionState{PeerCertificates: []*x509.Certificate{&cert}}
   112  
   113  	req := http.Request{TLS: &tls}
   114  
   115  	server := Server{allowedNames: []string{"name-trusted"}}
   116  	if err := server.validate(&req); err != nil {
   117  		t.Fatalf("No error expected for %q but encountered %q", cert.Subject.CommonName, err.Error())
   118  	}
   119  }
   120  
   121  func TestValidate_ClientAllowedViaSAN(t *testing.T) {
   122  	cert := testCertificate()
   123  	cert.Subject.CommonName = "name-any"
   124  
   125  	tls := tls.ConnectionState{PeerCertificates: []*x509.Certificate{&cert}}
   126  
   127  	req := http.Request{TLS: &tls}
   128  
   129  	server := Server{allowedNames: []string{"linkerd.io"}}
   130  	if err := server.validate(&req); err != nil {
   131  		t.Fatalf("No error expected for %q but encountered %q", cert.Subject.CommonName, err.Error())
   132  	}
   133  }
   134  
   135  func TestValidate_ClientNotAllowed(t *testing.T) {
   136  	cert := testCertificate()
   137  	cert.Subject.CommonName = "name-untrusted"
   138  
   139  	tls := tls.ConnectionState{PeerCertificates: []*x509.Certificate{&cert}}
   140  
   141  	req := http.Request{TLS: &tls}
   142  
   143  	server := Server{allowedNames: []string{"name-trusted"}}
   144  	if err := server.validate(&req); err == nil {
   145  		t.Fatalf("Expected request to be rejected for %q", cert.Subject.CommonName)
   146  	}
   147  }
   148  
   149  func TestIsSubjectAlternateName(t *testing.T) {
   150  	testCases := []struct {
   151  		name     string
   152  		expected bool
   153  	}{
   154  		{
   155  			name:     "linkerd.io",
   156  			expected: true,
   157  		},
   158  		{
   159  			name:     "root@localhost",
   160  			expected: true,
   161  		},
   162  		{
   163  			name:     "192.168.1.1",
   164  			expected: true,
   165  		},
   166  		{
   167  			name:     "http://localhost/api/test",
   168  			expected: true,
   169  		},
   170  		{
   171  			name:     "mystique",
   172  			expected: false,
   173  		},
   174  	}
   175  
   176  	cert := testCertificate()
   177  	for _, tc := range testCases {
   178  		tc := tc // pin
   179  		t.Run(tc.name, func(t *testing.T) {
   180  			actual := isSubjectAlternateName(&cert, tc.name)
   181  			if actual != tc.expected {
   182  				t.Fatalf("expected %t, but got %t", tc.expected, actual)
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  func testCertificate() x509.Certificate {
   189  	uri, _ := url.Parse("http://localhost/api/test")
   190  	cert := x509.Certificate{
   191  		Subject: pkix.Name{
   192  			CommonName: "linkerd-test",
   193  		},
   194  		DNSNames: []string{
   195  			"localhost",
   196  			"linkerd.io",
   197  		},
   198  		EmailAddresses: []string{
   199  			"root@localhost",
   200  		},
   201  		IPAddresses: []net.IP{
   202  			net.IPv4(127, 0, 0, 1),
   203  			net.IPv4(192, 168, 1, 1),
   204  		},
   205  		URIs: []*url.URL{
   206  			uri,
   207  		},
   208  	}
   209  	return cert
   210  }
   211  

View as plain text