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
Этот коммит содержится в:
Daniel Schalla
2018-10-16 16:51:46 +02:00
коммит произвёл Christopher Speller
родитель cedf6488e4
Коммит 557fd9ea18
9 изменённых файлов: 395 добавлений и 11 удалений

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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 Обычный файл
Просмотреть файл

@@ -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")
}
}