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
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
cedf6488e4
Коммит
557fd9ea18
@@ -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
|
||||
}
|
||||
|
||||
a.Srv.Server.TLSConfig = tlsConfig
|
||||
err = a.Srv.Server.ServeTLS(listener, "", "")
|
||||
} else {
|
||||
err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile)
|
||||
tlsConfig.CipherSuites = cipherSuites
|
||||
}
|
||||
|
||||
certFile := ""
|
||||
keyFile := ""
|
||||
|
||||
if *a.Config().ServiceSettings.UseLetsEncrypt {
|
||||
tlsConfig.GetCertificate = m.GetCertificate
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
} else {
|
||||
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)
|
||||
}()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
"ConnectionSecurity": "",
|
||||
"TLSCertFile": "",
|
||||
"TLSKeyFile": "",
|
||||
"TLSMinVer": "1.2",
|
||||
"TLSStrictTransport": false,
|
||||
"TLSStrictTransportMaxAge": 63072000,
|
||||
"TLSOverwriteCiphers": [],
|
||||
"UseLetsEncrypt": false,
|
||||
"LetsEncryptCertificateCacheFile": "./config/letsencrypt.cache",
|
||||
"Forward80To443": false,
|
||||
|
||||
12
i18n/en.json
12
i18n/en.json
@@ -4314,6 +4314,18 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Invalid value for webserver connection security."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.tls_cert_file.app_error",
|
||||
"translation": "Invalid value for TLS certificate file - Either use LetsEncrypt or set path to existing certificate file"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.tls_key_file.app_error",
|
||||
"translation": "Invalid value for TLS key file - Either use LetsEncrypt or set path to existing key file"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.tls_overwrite_cipher.app_error",
|
||||
"translation": "Invalid value passed for TLS overwrite cipher - Please refer to the documentation for valid values"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Websocket URL must be a valid URL and start with ws:// or wss://"
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -174,6 +176,31 @@ const (
|
||||
CLIENT_SIDE_CERT_CHECK_SECONDARY_AUTH = "secondary"
|
||||
)
|
||||
|
||||
var ServerTLSSupportedCiphers = map[string]uint16{
|
||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
|
||||
type ServiceSettings struct {
|
||||
SiteURL *string
|
||||
WebsocketURL *string
|
||||
@@ -182,6 +209,10 @@ type ServiceSettings struct {
|
||||
ConnectionSecurity *string
|
||||
TLSCertFile *string
|
||||
TLSKeyFile *string
|
||||
TLSMinVer *string
|
||||
TLSStrictTransport *bool
|
||||
TLSStrictTransportMaxAge *int64
|
||||
TLSOverwriteCiphers []string
|
||||
UseLetsEncrypt *bool
|
||||
LetsEncryptCertificateCacheFile *string
|
||||
Forward80To443 *bool
|
||||
@@ -324,6 +355,22 @@ func (s *ServiceSettings) SetDefaults() {
|
||||
s.TLSCertFile = NewString(SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE)
|
||||
}
|
||||
|
||||
if s.TLSMinVer == nil {
|
||||
s.TLSMinVer = NewString("1.2")
|
||||
}
|
||||
|
||||
if s.TLSStrictTransport == nil {
|
||||
s.TLSStrictTransport = NewBool(false)
|
||||
}
|
||||
|
||||
if s.TLSStrictTransportMaxAge == nil {
|
||||
s.TLSStrictTransportMaxAge = NewInt64(63072000)
|
||||
}
|
||||
|
||||
if s.TLSOverwriteCiphers == nil {
|
||||
s.TLSOverwriteCiphers = []string{}
|
||||
}
|
||||
|
||||
if s.UseLetsEncrypt == nil {
|
||||
s.UseLetsEncrypt = NewBool(false)
|
||||
}
|
||||
@@ -2352,6 +2399,32 @@ func (ss *ServiceSettings) isValid() *AppError {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.webserver_security.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *ss.ConnectionSecurity == CONN_SECURITY_TLS && *ss.UseLetsEncrypt == false {
|
||||
appErr := NewAppError("Config.IsValid", "model.config.is_valid.tls_cert_file.app_error", nil, "", http.StatusBadRequest)
|
||||
|
||||
if *ss.TLSCertFile == "" {
|
||||
return appErr
|
||||
} else if _, err := os.Stat(*ss.TLSCertFile); os.IsNotExist(err) {
|
||||
return appErr
|
||||
}
|
||||
|
||||
appErr = NewAppError("Config.IsValid", "model.config.is_valid.tls_key_file.app_error", nil, "", http.StatusBadRequest)
|
||||
|
||||
if *ss.TLSKeyFile == "" {
|
||||
return appErr
|
||||
} else if _, err := os.Stat(*ss.TLSKeyFile); os.IsNotExist(err) {
|
||||
return appErr
|
||||
}
|
||||
}
|
||||
|
||||
if len(ss.TLSOverwriteCiphers) > 0 {
|
||||
for _, cipher := range ss.TLSOverwriteCiphers {
|
||||
if _, ok := ServerTLSSupportedCiphers[cipher]; !ok {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.tls_overwrite_cipher.app_error", map[string]interface{}{"name": cipher}, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *ss.ReadTimeout <= 0 {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.read_timeout.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
20
tests/tls_test_cert.pem
Обычный файл
20
tests/tls_test_cert.pem
Обычный файл
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWjCCAkKgAwIBAgIJAK0E8sSdFSXnMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV
|
||||
BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
|
||||
Q29tcGFueSBMdGQwHhcNMTgxMDA2MjAwMzQ1WhcNMTkxMDA2MjAwMzQ1WjBCMQsw
|
||||
CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh
|
||||
dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
0VVolTOXKn3sVwKivDJH015wa4f3c+9OrCVwdg7ZTaiebDutLT2kHi/H8cElpi67
|
||||
cE8ios4hPYfF+jJpNF1VMuLUtq93UWwUYhb709AvArMSA5L5Vz+0z/VPysu03jrC
|
||||
o21A4U2G+LXjgOp8oSeRnfHRz6LrQxl1GTl2Kavou41SIpaf7YTM9za51Ya5wnUI
|
||||
6UoCEnsgI2qVgeHdF9dMpV5XY5VM0lROdoZqtpRjJxe5wFZkW6JOpWpirX1h6jPq
|
||||
KsY3CacCwonFrGM11mhcliBEGnUGEqNwBUrTkt2opTJ/L3KLFNC65ukg1y4RfFHz
|
||||
1uQ/IrpNjpClT4AnlwkAmQIDAQABo1MwUTAdBgNVHQ4EFgQUxI+gQOaOrxtW+wHG
|
||||
vFCqeipS1s8wHwYDVR0jBBgwFoAUxI+gQOaOrxtW+wHGvFCqeipS1s8wDwYDVR0T
|
||||
AQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKGXu7u23BMKw0sPjLB9Nu3vp
|
||||
0Ova9mflJ4mdI5Y1mClwdN6ysIibC3RDQZbcsz4oEFOh11+z+xLmADhBzEGksuDs
|
||||
9ZV2EAOvlFZpsl/2z1ACDq9NF6ru1slspOp2JRr74LIDo2iU4a1ubW6l1NN7bM3d
|
||||
2AWjKmeaqwyT80sGVO0ZCYO9QsIgX+WR4SyaY1xUATJEs8gW64jBzcJQSnZZbHjt
|
||||
eArVVzwUCAOp1XJhoXP/l9zOg/vtw8PUwb8iPZGK/8UtYTuHgVQUsYr6NAlds4Ag
|
||||
IQMsphOLsvz8tXEsQAvd3e0Sz94/oZP1aDUopom9gPDNGOBykuWW0epBfv950A==
|
||||
-----END CERTIFICATE-----
|
||||
28
tests/tls_test_key.pem
Обычный файл
28
tests/tls_test_key.pem
Обычный файл
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRVWiVM5cqfexX
|
||||
AqK8MkfTXnBrh/dz706sJXB2DtlNqJ5sO60tPaQeL8fxwSWmLrtwTyKiziE9h8X6
|
||||
Mmk0XVUy4tS2r3dRbBRiFvvT0C8CsxIDkvlXP7TP9U/Ky7TeOsKjbUDhTYb4teOA
|
||||
6nyhJ5Gd8dHPoutDGXUZOXYpq+i7jVIilp/thMz3NrnVhrnCdQjpSgISeyAjapWB
|
||||
4d0X10ylXldjlUzSVE52hmq2lGMnF7nAVmRbok6lamKtfWHqM+oqxjcJpwLCicWs
|
||||
YzXWaFyWIEQadQYSo3AFStOS3ailMn8vcosU0Lrm6SDXLhF8UfPW5D8iuk2OkKVP
|
||||
gCeXCQCZAgMBAAECggEBALRkQYexuabodO5WWx6KxdKkI4TG2ruRkd5PNSbHjQOb
|
||||
N0pV8tp1sCRDUK5In8UhqG0UBOj/cS2w/y6omniBpZYAWwZDFzOXS8lrvP+++4P8
|
||||
BJ4H3c8OGybKY0SDXw3S3UAwOiTtxk41kCPb7iKCEr5lUUT5RHvCSGLAXc9zUU+s
|
||||
3d4vscDTc/aLzm4e/hlnzA6pPQuz/GustgBDqonQRKTnKZ1pnzl7cBw0rOIJtmHe
|
||||
pViS8g2jtGMB5NoIFj4u08MwKuKJDmr+vQxjMkyx05x+aN4QIR/6b4kw46wridAJ
|
||||
zKIrMwWRXcFQWLs08LuTKmqwdmuDfdzHRJtnTCMvf7ECgYEA7n21yG6EOmeZWRJH
|
||||
5fSwwttXbxx0JrfWhoBam8/RSumnFx1VUj+0vafO6W+USa5HGWJJ5FFRnpA6/OmD
|
||||
Ywv9eHKUY3HtGXiZ536Pt/dPfBLX/FFzMlscCUAT6UvYOqytYngoKQqQeSeHDjPJ
|
||||
HC8QyAW/gKOLvd2wdBHNZOT7Vp8CgYEA4LOz7hqVgHmIEaVSY+4WNCW/m4PPx/oW
|
||||
as1D+V1eqLzzHDVDmivOGiyP+SIN9+BARCdTi8TPDkzkqOOYAt1kU5Neo3DoH9i3
|
||||
0qteQjy1xLA9UqAwOr16YpoPM/pBb6iwNTrj88Kcfs163hhMoDnarq6y1h9UYKgF
|
||||
vJH5cIeudccCgYBRdptLdYSxNoYJCNeKUwS16pp5F60NNKqQkvNgWaJSBnHO0XQ9
|
||||
fglM5y8kSbrLWD5tC0fWN3i7wuSDU3hPst7H78uEFHw6wRlBG9gXrOB3rzAbve6t
|
||||
erWe60Zh4Ehh8m3fPs/pBPTIjZnyXfoKKIGA8YWyeSrYlgsZ+qLAHf9EXQKBgQDb
|
||||
WnI26VqyrXFIkJQKm3yvcX5IKXfoJ1pE7pcB0sU6giHtko2o7kRnxsLRmQ37wb3b
|
||||
Cm0Dj5/1vNinim51tXxgHggQE4N2u1BP5xzAGpXzKXzjsR8D6L6VjQF0Y0QH5awG
|
||||
erPW3U96dcsRDrWW4IN7bW2Fm9X5+WyINhREZx/HNwKBgH/RMcGhiIcpI4IPGmNi
|
||||
udFhUd60izh5Gimyg8yBSPmX1xI6zjwPXiUvHpPWz65OZk1eltZgoDYvSe/rauJv
|
||||
8VtdisTqZwUQG5mogQxo6uofjzKqQQLiJtmOrL1ZzJU8vJNKdiJTIs22wrSxTenj
|
||||
QtIQkWnBO5bOVY/99s5wlzKw
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -75,6 +75,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
|
||||
w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.License() != nil))
|
||||
|
||||
if *c.App.Config().ServiceSettings.TLSStrictTransport {
|
||||
w.Header().Set("Strict-Transport-Security", fmt.Sprintf("max-age=%d", *c.App.Config().ServiceSettings.TLSStrictTransportMaxAge))
|
||||
}
|
||||
|
||||
if h.IsStatic {
|
||||
// Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking
|
||||
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func handlerForTest(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
func handlerForHTTPErrors(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.Err = model.NewAppError("loginWithSaml", "api.user.saml.not_available.app_error", nil, "", http.StatusFound)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestHandlerServeHTTPErrors(t *testing.T) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
handler := web.NewHandler(handlerForTest)
|
||||
handler := web.NewHandler(handlerForHTTPErrors)
|
||||
|
||||
var flagtests = []struct {
|
||||
name string
|
||||
@@ -57,3 +57,50 @@ func TestHandlerServeHTTPErrors(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func handlerForHTTPSecureTransport(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func TestHandlerServeHTTPSecureTransport(t *testing.T) {
|
||||
a, err := app.New(app.StoreOverride(testStore), app.DisableConfigWatch)
|
||||
defer a.Shutdown()
|
||||
|
||||
a.UpdateConfig(func(config *model.Config) {
|
||||
*config.ServiceSettings.TLSStrictTransport = true
|
||||
*config.ServiceSettings.TLSStrictTransportMaxAge = 6000
|
||||
})
|
||||
|
||||
web := NewWeb(a, a.Srv.Router)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
handler := web.NewHandler(handlerForHTTPSecureTransport)
|
||||
|
||||
request := httptest.NewRequest("GET", "/api/v4/test", nil)
|
||||
|
||||
response := httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
header := response.Header().Get("Strict-Transport-Security")
|
||||
|
||||
if header == "" {
|
||||
t.Errorf("Strict-Transport-Security expected but not existent")
|
||||
}
|
||||
|
||||
if header != "max-age=6000" {
|
||||
t.Errorf("Expected max-age=6000, got %s", header)
|
||||
}
|
||||
|
||||
a.UpdateConfig(func(config *model.Config) {
|
||||
*config.ServiceSettings.TLSStrictTransport = false
|
||||
})
|
||||
|
||||
request = httptest.NewRequest("GET", "/api/v4/test", nil)
|
||||
|
||||
response = httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
header = response.Header().Get("Strict-Transport-Security")
|
||||
|
||||
if header != "" {
|
||||
t.Errorf("Strict-Transport-Security header is not expected, but returned")
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user