diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5c6fa4c4c4..38998987591 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,6 +20,7 @@ repos: (?x)^( config/tls/devlocal-.+\.key$| pkg/server/testdata/localhost.key$| + pkg/server/testdata/localhost-invalid.key$| pkg/server/testdata/officelocal.key$| pkg/auth/authentication/auth_test.go$| pkg/cli/auth.go$| diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index f357f12f3f4..4a0cc51b6d2 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -3,6 +3,7 @@ package server import ( "crypto/tls" "crypto/x509" + "fmt" "io/ioutil" "log" "net/http" @@ -186,3 +187,191 @@ func (suite *serverSuite) TestHTTPServerConfig() { suite.Equal(httpsServer.Addr, "127.0.0.1:8080") suite.Equal(suite.httpHandler, httpsServer.Handler) } + +func (suite *serverSuite) TestTLSConfigWithRequest() { + + keyPair, err := tls.X509KeyPair( + suite.readFile("localhost.pem"), + suite.readFile("localhost.key")) + suite.NoError(err) + certificates := []tls.Certificate{keyPair} + + caFile := suite.readFile("ca.pem") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caFile) + + // A handler that we can test with + htmlBody := "Hello, client" + httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Check the TLS connection from inside the request + connState := r.TLS + suite.Equal(tls.VersionTLS12, int(connState.Version)) + suite.True(connState.HandshakeComplete) + suite.False(connState.DidResume) + suite.Equal("", connState.NegotiatedProtocol) + suite.True(connState.NegotiatedProtocolIsMutual) + suite.Equal("localhost", connState.ServerName) + suite.Equal("Snake Oil", connState.PeerCertificates[0].Subject.Organization[0]) + suite.Equal("Snake Oil", connState.VerifiedChains[0][0].Subject.Organization[0]) + + // Now write out a message + fmt.Fprintln(w, htmlBody) + }) + + host := "localhost" + port := 7443 + srv, err := CreateNamedServer(&CreateNamedServerInput{ + Host: host, + Port: port, + HTTPHandler: httpHandler, + Logger: suite.logger, + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: certificates, + ClientCAs: caCertPool, + }) + defer srv.Close() + suite.NoError(err) + + // Start the Server + go srv.ListenAndServeTLS() + + // Send a request + config := tls.Config{ + RootCAs: caCertPool, + Certificates: certificates, + } + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &config, + }, + } + res, err := client.Get(fmt.Sprintf("https://%s:%d", host, port)) + suite.NoError(err) + + // Read the response + if res != nil { + body, bodyErr := ioutil.ReadAll(res.Body) + res.Body.Close() + suite.NoError(bodyErr) + suite.Equal(htmlBody+"\n", string(body)) + } + + // Check the TLS connection directly + conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &config) + defer conn.Close() + suite.NoError(err) +} + +func (suite *serverSuite) TestTLSConfigWithRequestNoClientAuth() { + + keyPair, err := tls.X509KeyPair( + suite.readFile("localhost.pem"), + suite.readFile("localhost.key")) + suite.NoError(err) + certificates := []tls.Certificate{keyPair} + + caFile := suite.readFile("ca.pem") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caFile) + + // A handler that we can test with + httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + panic("This handler should have never fired") + }) + + host := "localhost" + port := 7443 + srv, err := CreateNamedServer(&CreateNamedServerInput{ + Host: host, + Port: port, + HTTPHandler: httpHandler, + Logger: suite.logger, + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: certificates, + ClientCAs: caCertPool, + }) + defer srv.Close() + suite.NoError(err) + + // Start the Server + go srv.ListenAndServeTLS() + + // Send a request without TLS client side cert configuration, should return error + client := &http.Client{ + Transport: &http.Transport{}, + } + res, err := client.Get(fmt.Sprintf("https://%s:%d", host, port)) + suite.Nil(res) + suite.Error(err) + + // Check the TLS connection directly + // This should fail and conn should be nil + config := tls.Config{} + conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &config) + suite.Nil(conn) + suite.Error(err) +} + +func (suite *serverSuite) TestTLSConfigWithInvalidAuth() { + + keyPair, err := tls.X509KeyPair( + suite.readFile("localhost.pem"), + suite.readFile("localhost.key")) + suite.NoError(err) + certificates := []tls.Certificate{keyPair} + + caFile := suite.readFile("ca.pem") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caFile) + + // A handler that we can test with + httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + panic("This handler should have never fired") + }) + + host := "localhost" + port := 7443 + srv, err := CreateNamedServer(&CreateNamedServerInput{ + Host: host, + Port: port, + HTTPHandler: httpHandler, + Logger: suite.logger, + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: certificates, + ClientCAs: caCertPool, + }) + defer srv.Close() + suite.NoError(err) + + // Start the Server + go srv.ListenAndServeTLS() + + // Send a request with self signed cert and CA, but doesn't match server used CA + invalidKeyPair, err := tls.X509KeyPair( + suite.readFile("localhost-invalid.pem"), + suite.readFile("localhost-invalid.key")) + suite.NoError(err) + invalidCertificates := []tls.Certificate{invalidKeyPair} + + invalidCaFile := suite.readFile("invalid-ca.pem") + invalidCaCertPool := x509.NewCertPool() + invalidCaCertPool.AppendCertsFromPEM(invalidCaFile) + + config := tls.Config{ + RootCAs: invalidCaCertPool, + Certificates: invalidCertificates, + } + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &config, + }, + } + res, err := client.Get(fmt.Sprintf("https://%s:%d", host, port)) + suite.Nil(res) + suite.Error(err) + + // Check the TLS connection directly + conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &config) + suite.Nil(conn) + suite.Error(err) +} diff --git a/pkg/server/testdata/invalid-ca.pem b/pkg/server/testdata/invalid-ca.pem new file mode 100644 index 00000000000..a850ef20569 --- /dev/null +++ b/pkg/server/testdata/invalid-ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFzCCAf8CFGcafzHmo1hms/qcCZTGwhAYbBoZMA0GCSqGSIb3DQEBCwUAMEgx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRAwDgYDVQQKDAdFbXUg +T2lsMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkxMDA3MjAwMTMzWhcNMjAxMDA2 +MjAwMTMzWjBIMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEQMA4G +A1UECgwHRW11IE9pbDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAz6KUHCumeRYHo/o/epsJfjG4Nr3Xb2YotsD46Trn +W/HIQrE7alPIfy7nRO6sLc+Doew52BvvD2Dp5HwRTmijKyEtIoOo8hUHC56XX/DF +M9HvDl7v5I5y+z/otxqv0AKYfB+ElAM3TU6txzqGuFedVQedauukX3EkE3XlvKR8 +IjUocJ7fsjg4Ry9CgjiXA1l5Z/wcrA0+ewyF3FbFSB78+2qtBayW6TUxF1FzeJIi +cfAOkN/EIcBjrbC4qb5h1zyoWNATa9MHJnkZzmYQOZqj8BKJbE6F9frnxd3bZP4I +z+j0/+xLzCBa3nXz/0pohvJwgRrqX89Vw2gjreTgOvY/8QIDAQABMA0GCSqGSIb3 +DQEBCwUAA4IBAQAFBf6B8tJdIlQ+Qz9Se0rbMUqvMmxanU4l2VbRVoDc3o5LxJZy +1D0AqHZd6KlKOyHzTY0V2VsYAaw8aDoqKDnrStLCyJUaHdLVpS82PVQ9JabdyHfR +SUI3TyHm7zc9pvsdqC73uF57/PmNhABV08y7axBXDxuVor644/BFI0fLUuHOVG6c +OEWqtEEjbNWbiqky7hRzqkp0hglX0AyFn10iDQOn4Bg0nvCwBGqtHrOBzodn5/L4 +zFQNsZkorsUTejpHLKszrjFJPg4axpz0M1ytBrrCDxsDShDkmlXeKt3CKfd1+G3X +SRn4lM+fG7hbFCencytzSECFrKCRFxJdET+z +-----END CERTIFICATE----- diff --git a/pkg/server/testdata/localhost-invalid.key b/pkg/server/testdata/localhost-invalid.key new file mode 100644 index 00000000000..03eecc8bdac --- /dev/null +++ b/pkg/server/testdata/localhost-invalid.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAz6KUHCumeRYHo/o/epsJfjG4Nr3Xb2YotsD46TrnW/HIQrE7 +alPIfy7nRO6sLc+Doew52BvvD2Dp5HwRTmijKyEtIoOo8hUHC56XX/DFM9HvDl7v +5I5y+z/otxqv0AKYfB+ElAM3TU6txzqGuFedVQedauukX3EkE3XlvKR8IjUocJ7f +sjg4Ry9CgjiXA1l5Z/wcrA0+ewyF3FbFSB78+2qtBayW6TUxF1FzeJIicfAOkN/E +IcBjrbC4qb5h1zyoWNATa9MHJnkZzmYQOZqj8BKJbE6F9frnxd3bZP4Iz+j0/+xL +zCBa3nXz/0pohvJwgRrqX89Vw2gjreTgOvY/8QIDAQABAoIBAQCMOMxzddsYHRON +3vutrjQsACHa/jwWpFRMj2fKApiqVE+dEpoAhZJ6qc9Pkyu+SE0qNiEeXEcHCLkM +L+l0HWBMSTx0+ZtH2YGHjFPIXA6hn14lQ2IY5UarfQ6JUcF35bja4mIjn0RitgbP +7Ct9JNZAzUK3KWo5dmwZpF5BFogqhKoriOd2d3sEg+ixeMz6CJBxSqT4vGHjQH8U +oCqfDsLQQzLrX/KGCDMfOUa3LJ9Gbx7uqAM+lURraIpmiBxfw2Rws7ji4tv7f658 +sninP6QMFMWoRINH+CBoeogdqVsjCn6dujlG8PoZh1EKGF2w3Wur3dHEDXQveYBv +1KCJwM/RAoGBAO9NTYa1eBr+c+Cxv2BWF1trB95kkJI3O+2uVzTWZ94sEThO7X// +RV+EV59+YafKrm4rw+pwUI0imLhKBwCCF+9/FM/bRgaBQ5FfiBSnYTMRSbQqbjCG +x3BneaTeBTHl8U/KQqDebs2Ur9TvESSKafoSkYOgDGbiV4RjiGVMSFY9AoGBAN4f +mpqK490CxfXGrZeUUF9NqlZ+xobj0ywYvXwfjKphiCN740sBf0aekh5tljDFs6Wl +8wJYqjA6bDPD+ELqIHJIlV/bbA6N2s8SkGvUz0P/oLuveRzYbHSGc+Voz5Ik4YLu +enUilQwmD20ZvHraebXBPEWlr3BwOtdUvk+YbJ/FAoGADBYNalZUAJkFZT3U+pqe +h2F6fkL6AkiD4FdD1ElX6aK0I/HokORlrpNSkZ2idQUJlkrjlD2j89zhdb1XaTEo +4FG8zVq3yLBSp0754oEpqZif7leORSkpfYXcYPVNsd15OJxwjQtu8TcnQkvow72x +6I1ziABXYALihK86PlOB+8kCgYAywSfzji747kxLl6VM0H/49ucoMnmsktxK5DBP +72mV+QlBimpqViI+AbfDqii6ZkJhZGT9sULaGE9nGK1epN7bHki7iYjMXo+T51sG +uK7Pi9avbC4RcEZ/7cUHs4ZSj4jUASgMIeXKy9uUg1/09nz/EfNyuqQ23f4j++Is +j9iqPQKBgQC9nVyo3qrwVY3idfs5QO71oFMq44+UdS76ZsH+B7meeWlfOo1FuXSw +a5FMO6iwjkRta5YnWtTUMqmBVO5FlOTTSF6XggHxbsXOmmFFi9UOxSmwqq34m0wE +2HwUF2cbGMOCHPxqRK7GvVL5jMrcPvzp6120qLC72J25keuOr7YuNQ== +-----END RSA PRIVATE KEY----- diff --git a/pkg/server/testdata/localhost-invalid.pem b/pkg/server/testdata/localhost-invalid.pem new file mode 100644 index 00000000000..a850ef20569 --- /dev/null +++ b/pkg/server/testdata/localhost-invalid.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFzCCAf8CFGcafzHmo1hms/qcCZTGwhAYbBoZMA0GCSqGSIb3DQEBCwUAMEgx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRAwDgYDVQQKDAdFbXUg +T2lsMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkxMDA3MjAwMTMzWhcNMjAxMDA2 +MjAwMTMzWjBIMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEQMA4G +A1UECgwHRW11IE9pbDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAz6KUHCumeRYHo/o/epsJfjG4Nr3Xb2YotsD46Trn +W/HIQrE7alPIfy7nRO6sLc+Doew52BvvD2Dp5HwRTmijKyEtIoOo8hUHC56XX/DF +M9HvDl7v5I5y+z/otxqv0AKYfB+ElAM3TU6txzqGuFedVQedauukX3EkE3XlvKR8 +IjUocJ7fsjg4Ry9CgjiXA1l5Z/wcrA0+ewyF3FbFSB78+2qtBayW6TUxF1FzeJIi +cfAOkN/EIcBjrbC4qb5h1zyoWNATa9MHJnkZzmYQOZqj8BKJbE6F9frnxd3bZP4I +z+j0/+xLzCBa3nXz/0pohvJwgRrqX89Vw2gjreTgOvY/8QIDAQABMA0GCSqGSIb3 +DQEBCwUAA4IBAQAFBf6B8tJdIlQ+Qz9Se0rbMUqvMmxanU4l2VbRVoDc3o5LxJZy +1D0AqHZd6KlKOyHzTY0V2VsYAaw8aDoqKDnrStLCyJUaHdLVpS82PVQ9JabdyHfR +SUI3TyHm7zc9pvsdqC73uF57/PmNhABV08y7axBXDxuVor644/BFI0fLUuHOVG6c +OEWqtEEjbNWbiqky7hRzqkp0hglX0AyFn10iDQOn4Bg0nvCwBGqtHrOBzodn5/L4 +zFQNsZkorsUTejpHLKszrjFJPg4axpz0M1ytBrrCDxsDShDkmlXeKt3CKfd1+G3X +SRn4lM+fG7hbFCencytzSECFrKCRFxJdET+z +-----END CERTIFICATE-----