github.com/avenga/couper@v1.12.2/server/http_mtls_test.go (about)

     1  package server_test
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"net/http"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/avenga/couper/internal/test"
    13  	"github.com/avenga/couper/server"
    14  )
    15  
    16  func TestHTTPSServer_TLS_SelfSigned(t *testing.T) {
    17  	helper := test.New(t)
    18  
    19  	client := test.NewHTTPSClient(&tls.Config{
    20  		RootCAs: x509.NewCertPool(),
    21  	})
    22  
    23  	shutdown, _ := newCouper("testdata/mtls/01_couper.hcl", helper)
    24  	defer shutdown()
    25  
    26  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
    27  	helper.Must(err)
    28  
    29  	_, err = client.Do(outreq)
    30  	if err == nil {
    31  		t.Fatal("tls error expected, got nil")
    32  	}
    33  
    34  	if err.Error() != `Get "https://localhost:4443/": x509: certificate signed by unknown authority` {
    35  		t.Errorf("Want unknown authority error, got: %v", err)
    36  	}
    37  }
    38  
    39  func TestHTTPSServer_TLS_ServerCertificate(t *testing.T) {
    40  	helper := test.New(t)
    41  
    42  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
    43  	helper.Must(err)
    44  
    45  	pool := x509.NewCertPool()
    46  	pool.AddCert(selfSigned.CA.Leaf)
    47  	client := test.NewHTTPSClient(&tls.Config{
    48  		RootCAs: pool,
    49  	})
    50  
    51  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/02_couper.hcl", helper, map[string]interface{}{
    52  		"publicKey":  string(selfSigned.ServerCertificate.Certificate), // PEM
    53  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),  // PEM
    54  	})
    55  	helper.Must(err)
    56  	defer shutdown()
    57  
    58  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
    59  	helper.Must(err)
    60  
    61  	res, err := client.Do(outreq)
    62  	helper.Must(err)
    63  
    64  	if res.StatusCode != http.StatusOK {
    65  		t.Errorf("Expected statusOK, got: %d", res.StatusCode)
    66  	}
    67  
    68  	if !strings.HasPrefix(res.Header.Get("Location"), "https://") {
    69  		t.Errorf("Expected https scheme in url variable value, got: %s", res.Header.Get("Location"))
    70  	}
    71  }
    72  
    73  func TestHTTPSServer_TLS_ServerClientCertificate(t *testing.T) {
    74  	helper := test.New(t)
    75  
    76  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
    77  	helper.Must(err)
    78  
    79  	pool := x509.NewCertPool()
    80  	pool.AddCert(selfSigned.CA.Leaf)
    81  	client := test.NewHTTPSClient(&tls.Config{
    82  		RootCAs:      pool,
    83  		Certificates: []tls.Certificate{*selfSigned.Client},
    84  	})
    85  
    86  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/03_couper.hcl", helper, map[string]interface{}{
    87  		"publicKey":  string(selfSigned.ServerCertificate.Certificate),             // PEM
    88  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),              // PEM
    89  		"clientCA":   string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM
    90  	})
    91  	helper.Must(err)
    92  	defer shutdown()
    93  
    94  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
    95  	helper.Must(err)
    96  
    97  	res, err := client.Do(outreq)
    98  	helper.Must(err)
    99  
   100  	if res.StatusCode != http.StatusOK {
   101  		t.Errorf("Expected statusOK, got: %d", res.StatusCode)
   102  	}
   103  
   104  	// without presenting the client certificate
   105  	client = test.NewHTTPSClient(&tls.Config{
   106  		RootCAs: pool,
   107  	})
   108  
   109  	_, err = client.Do(outreq)
   110  
   111  	var RemoteFailedCertCheck = func(err error) bool {
   112  		prefix := `Get "https://localhost:4443/": `
   113  		suffix := "remote error: tls: bad certificate"
   114  		return err != nil && (err.Error() == prefix+"readLoopPeekFailLocked: "+suffix ||
   115  			err.Error() == prefix+suffix)
   116  	}
   117  
   118  	if !RemoteFailedCertCheck(err) {
   119  		t.Errorf("Expected a tls handshake error, got: %v", err)
   120  	}
   121  }
   122  
   123  func TestHTTPSServer_TLS_ServerClientCertificateLeaf(t *testing.T) {
   124  	helper := test.New(t)
   125  
   126  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
   127  	helper.Must(err)
   128  
   129  	pool := x509.NewCertPool()
   130  	pool.AddCert(selfSigned.CA.Leaf)
   131  	client := test.NewHTTPSClient(&tls.Config{
   132  		RootCAs:      pool,
   133  		Certificates: []tls.Certificate{*selfSigned.Client},
   134  	})
   135  
   136  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/04_couper.hcl", helper, map[string]interface{}{
   137  		"publicKey":  string(selfSigned.ServerCertificate.Certificate),             // PEM
   138  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),              // PEM
   139  		"clientCA":   string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM
   140  		"clientLeaf": string(selfSigned.ClientCertificate.Certificate),             // PEM
   141  	})
   142  	helper.Must(err)
   143  	defer shutdown()
   144  
   145  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
   146  	helper.Must(err)
   147  
   148  	res, err := client.Do(outreq)
   149  	helper.Must(err)
   150  
   151  	if res.StatusCode != http.StatusOK {
   152  		t.Errorf("Expected statusOK, got: %d", res.StatusCode)
   153  	}
   154  }
   155  
   156  func TestHTTPSServer_TLS_ServerClientCertificateLeafNoMatch(t *testing.T) {
   157  	helper := test.New(t)
   158  
   159  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
   160  	helper.Must(err)
   161  
   162  	pool := x509.NewCertPool()
   163  	pool.AddCert(selfSigned.CA.Leaf)
   164  	client := test.NewHTTPSClient(&tls.Config{
   165  		RootCAs:      pool,
   166  		Certificates: []tls.Certificate{*selfSigned.Client},
   167  	})
   168  
   169  	shutdown, hook, err := newCouperWithTemplate("testdata/mtls/04_couper.hcl", helper, map[string]interface{}{
   170  		"publicKey":  string(selfSigned.ServerCertificate.Certificate),             // PEM
   171  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),              // PEM
   172  		"clientCA":   string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM
   173  		"clientLeaf": string(selfSigned.CACertificate.Certificate),                 // PEM / just a non-matching one
   174  	})
   175  	helper.Must(err)
   176  	defer shutdown()
   177  
   178  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
   179  	helper.Must(err)
   180  
   181  	hook.Reset()
   182  
   183  	_, err = client.Do(outreq)
   184  	if err == nil {
   185  		t.Error("expected a tls handshake error")
   186  	}
   187  
   188  	entries := hook.AllEntries()
   189  	if len(entries) == 0 {
   190  		t.Fatal("expected log entries")
   191  	}
   192  
   193  	if !strings.HasSuffix(entries[0].Message, "tls: client leaf certificate mismatch") {
   194  		t.Errorf("expected leaf mismatch err, got: %v", entries[0].Message)
   195  	}
   196  }
   197  
   198  func TestHTTPSServer_TLS_ServerClientLeafOnly(t *testing.T) {
   199  	helper := test.New(t)
   200  
   201  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
   202  	helper.Must(err)
   203  
   204  	pool := x509.NewCertPool()
   205  	pool.AddCert(selfSigned.CA.Leaf)
   206  	client := test.NewHTTPSClient(&tls.Config{
   207  		RootCAs:      pool,
   208  		Certificates: []tls.Certificate{*selfSigned.Client},
   209  	})
   210  
   211  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/06_couper.hcl", helper, map[string]interface{}{
   212  		"publicKey":  string(selfSigned.ServerCertificate.Certificate), // PEM
   213  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),  // PEM
   214  		"clientLeaf": string(selfSigned.ClientCertificate.Certificate), // PEM
   215  	})
   216  	helper.Must(err)
   217  	defer shutdown()
   218  
   219  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
   220  	helper.Must(err)
   221  
   222  	res, err := client.Do(outreq)
   223  	helper.Must(err)
   224  
   225  	if res.StatusCode != http.StatusOK {
   226  		t.Errorf("Expected statusOK, got: %d", res.StatusCode)
   227  	}
   228  }
   229  
   230  func TestHTTPSServer_TLS_ServerClientCertificateLeafMixed(t *testing.T) {
   231  	helper := test.New(t)
   232  
   233  	selfSigned1, err := server.NewCertificate(time.Hour, nil, nil)
   234  	helper.Must(err)
   235  
   236  	selfSigned2, err := server.NewCertificate(time.Hour, nil, nil)
   237  	helper.Must(err)
   238  
   239  	selfSigned3, err := server.NewCertificate(time.Hour, nil, nil)
   240  	helper.Must(err)
   241  
   242  	pool := x509.NewCertPool()
   243  	pool.AddCert(selfSigned1.CA.Leaf)
   244  
   245  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/07_couper.hcl", helper, map[string]interface{}{
   246  		"publicKey":    string(selfSigned1.ServerCertificate.Certificate),             // PEM
   247  		"privateKey":   string(selfSigned1.ServerCertificate.PrivateKey),              // PEM
   248  		"client1_Leaf": string(selfSigned1.ClientCertificate.Certificate),             // PEM
   249  		"client2_CA":   string(selfSigned2.ClientIntermediateCertificate.Certificate), // PEM
   250  		"client2_Leaf": string(selfSigned2.ClientCertificate.Certificate),             // PEM
   251  		"client3_CA":   string(selfSigned3.ClientIntermediateCertificate.Certificate), // PEM
   252  	})
   253  	helper.Must(err)
   254  	defer shutdown()
   255  
   256  	for i, clientCert := range []*tls.Certificate{selfSigned1.Client, selfSigned2.Client, selfSigned3.Client} {
   257  		t.Run(fmt.Sprintf("client%d", i+1), func(st *testing.T) {
   258  			h := test.New(st)
   259  			c := clientCert
   260  			client := test.NewHTTPSClient(&tls.Config{
   261  				RootCAs: pool,
   262  				GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
   263  					return c, nil
   264  				},
   265  			})
   266  
   267  			outreq, e := http.NewRequest(http.MethodGet, "https://localhost:4443/", nil)
   268  			h.Must(e)
   269  
   270  			res, e := client.Do(outreq)
   271  			h.Must(e)
   272  
   273  			if res.StatusCode != http.StatusOK {
   274  				st.Errorf("Expected statusOK, got: %d", res.StatusCode)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func TestHTTPSServer_TLS_ServerBackendClient(t *testing.T) {
   281  	helper := test.New(t)
   282  
   283  	selfSigned, err := server.NewCertificate(time.Minute, nil, nil)
   284  	helper.Must(err)
   285  
   286  	pool := x509.NewCertPool()
   287  	pool.AddCert(selfSigned.CA.Leaf)
   288  	client := test.NewHTTPSClient(&tls.Config{
   289  		RootCAs:      pool,
   290  		Certificates: []tls.Certificate{*selfSigned.Client},
   291  	})
   292  
   293  	shutdown, _, err := newCouperWithTemplate("testdata/mtls/05_couper.hcl", helper, map[string]interface{}{
   294  		"publicKey":  string(selfSigned.ServerCertificate.Certificate),             // PEM
   295  		"privateKey": string(selfSigned.ServerCertificate.PrivateKey),              // PEM
   296  		"clientCA":   string(selfSigned.ClientIntermediateCertificate.Certificate), // PEM
   297  		"clientLeaf": string(selfSigned.ClientCertificate.Certificate),             // PEM
   298  		"clientKey":  string(selfSigned.ClientCertificate.PrivateKey),              // PEM
   299  		"rootCA":     string(selfSigned.CACertificate.Certificate),                 // PEM
   300  	})
   301  	helper.Must(err)
   302  	defer shutdown()
   303  
   304  	outreq, err := http.NewRequest(http.MethodGet, "https://localhost:4443/inception", nil)
   305  	helper.Must(err)
   306  
   307  	res, err := client.Do(outreq)
   308  	helper.Must(err)
   309  
   310  	if res.StatusCode != http.StatusOK {
   311  		t.Errorf("Expected statusOK, got: %d", res.StatusCode)
   312  	}
   313  }