From 557fd9ea187b1279b43ff63b94fedf2320aa3351 Mon Sep 17 00:00:00 2001 From: Daniel Schalla Date: Tue, 16 Oct 2018 16:51:46 +0200 Subject: Set default ciphers, set tls 1.2 via config, set curve prefs (#9315) Config Checks at StartUp Part1 Config Checks; Tests for TLS Server HSTS header implementation + tests make gofmt happy with new go version... make gofmt happy with new go version #2... fix logic bug fix typo Fix unnecessary code block --- app/server.go | 65 ++++++++++++++++++++--- app/server_test.go | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/server.go b/app/server.go index debb6764f..b95059c84 100644 --- a/app/server.go +++ b/app/server.go @@ -46,7 +46,7 @@ type Server struct { didFinishListen chan struct{} } -var corsAllowedMethods []string = []string{ +var corsAllowedMethods = []string{ "POST", "GET", "OPTIONS", @@ -199,26 +199,75 @@ func (a *App) StartServer() error { go func() { var err error if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { - if *a.Config().ServiceSettings.UseLetsEncrypt { - tlsConfig := &tls.Config{ - GetCertificate: m.GetCertificate, + tlsConfig := &tls.Config{ + PreferServerCipherSuites: true, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + } + + switch *a.Config().ServiceSettings.TLSMinVer { + case "1.0": + tlsConfig.MinVersion = tls.VersionTLS10 + case "1.1": + tlsConfig.MinVersion = tls.VersionTLS11 + default: + tlsConfig.MinVersion = tls.VersionTLS12 + } + + defaultCiphers := []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + } + + if len(a.Config().ServiceSettings.TLSOverwriteCiphers) == 0 { + tlsConfig.CipherSuites = defaultCiphers + } else { + var cipherSuites []uint16 + for _, cipher := range a.Config().ServiceSettings.TLSOverwriteCiphers { + value, ok := model.ServerTLSSupportedCiphers[cipher] + + if !ok { + mlog.Warn("Unsupported cipher passed", mlog.String("cipher", cipher)) + continue + } + + cipherSuites = append(cipherSuites, value) } - tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") + if len(cipherSuites) == 0 { + mlog.Warn("No supported ciphers passed, fallback to default cipher suite") + cipherSuites = defaultCiphers + } + + tlsConfig.CipherSuites = cipherSuites + } + + certFile := "" + keyFile := "" - a.Srv.Server.TLSConfig = tlsConfig - err = a.Srv.Server.ServeTLS(listener, "", "") + if *a.Config().ServiceSettings.UseLetsEncrypt { + tlsConfig.GetCertificate = m.GetCertificate + tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") } else { - err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile) + certFile = *a.Config().ServiceSettings.TLSCertFile + keyFile = *a.Config().ServiceSettings.TLSKeyFile } + + a.Srv.Server.TLSConfig = tlsConfig + err = a.Srv.Server.ServeTLS(listener, certFile, keyFile) } else { err = a.Srv.Server.Serve(listener) } + if err != nil && err != http.ErrServerClosed { mlog.Critical(fmt.Sprintf("Error starting server, err:%v", err)) time.Sleep(time.Second) } + close(a.Srv.didFinishListen) }() diff --git a/app/server_test.go b/app/server_test.go index 94771a44e..4a355e113 100644 --- a/app/server_test.go +++ b/app/server_test.go @@ -4,6 +4,12 @@ package app import ( + "crypto/tls" + "github.com/mattermost/mattermost-server/utils" + "net/http" + "path" + "strconv" + "strings" "testing" "github.com/mattermost/mattermost-server/model" @@ -16,6 +22,10 @@ func TestStartServerSuccess(t *testing.T) { a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) serverErr := a.StartServer() + + client := &http.Client{} + checkEndpoint(t, client, "http://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + a.Shutdown() require.NoError(t, serverErr) } @@ -48,3 +58,140 @@ func TestStartServerPortUnavailable(t *testing.T) { a.Shutdown() require.Error(t, serverErr) } + +func TestStartServerTLSSuccess(t *testing.T) { + a, err := New() + require.NoError(t, err) + + testDir, _ := utils.FindDir("tests") + a.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.ListenAddress = ":0" + *cfg.ServiceSettings.ConnectionSecurity = "TLS" + *cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem") + *cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem") + }) + serverErr := a.StartServer() + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Transport: tr} + checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + + a.Shutdown() + require.NoError(t, serverErr) +} + +func TestStartServerTLSVersion(t *testing.T) { + a, err := New() + require.NoError(t, err) + + testDir, _ := utils.FindDir("tests") + a.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.ListenAddress = ":0" + *cfg.ServiceSettings.ConnectionSecurity = "TLS" + *cfg.ServiceSettings.TLSMinVer = "1.2" + *cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem") + *cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem") + }) + serverErr := a.StartServer() + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + MaxVersion: tls.VersionTLS11, + }, + } + + client := &http.Client{Transport: tr} + err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + + if !strings.Contains(err.Error(), "remote error: tls: protocol version not supported") { + t.Errorf("Expected protocol version error, got %s", err) + } + + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + + err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + + if err != nil { + t.Errorf("Expected nil, got %s", err) + } + + a.Shutdown() + require.NoError(t, serverErr) +} + +func TestStartServerTLSOverwriteCipher(t *testing.T) { + a, err := New() + require.NoError(t, err) + + testDir, _ := utils.FindDir("tests") + a.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.ListenAddress = ":0" + *cfg.ServiceSettings.ConnectionSecurity = "TLS" + cfg.ServiceSettings.TLSOverwriteCiphers = []string{ + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + } + *cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem") + *cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem") + }) + serverErr := a.StartServer() + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + }, + }, + } + + client := &http.Client{Transport: tr} + err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + + if !strings.Contains(err.Error(), "remote error: tls: handshake failure") { + t.Errorf("Expected protocol version error, got %s", err) + } + + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + }, + } + + err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound) + + if err != nil { + t.Errorf("Expected nil, got %s", err) + } + + a.Shutdown() + require.NoError(t, serverErr) +} + +func checkEndpoint(t *testing.T, client *http.Client, url string, expectedStatus int) error { + res, err := client.Get(url) + + if err != nil { + return err + } + + defer res.Body.Close() + + if res.StatusCode != expectedStatus { + t.Errorf("Response code was %d; want %d", res.StatusCode, expectedStatus) + } + + return nil +} -- cgit v1.2.3-1-g7c22