server/mux: PeekProtocol() should return error and connection be closed. (#3608)

For TLS peekProtocol do not assume the incoming request to be a TLS
connection perform a handshake() instead and validate.

Also add some security related defaults to `tls.Config`.
master
Harshavardhana 8 years ago committed by GitHub
parent 51fa4f7fe3
commit 3640c63289
  1. 96
      cmd/server-mux.go

@ -71,6 +71,7 @@ func NewConnMux(c net.Conn) *ConnMux {
} }
} }
// List of protocols to be detected by PeekProtocol function.
const ( const (
protocolTLS = "tls" protocolTLS = "tls"
protocolHTTP1 = "http" protocolHTTP1 = "http"
@ -78,26 +79,33 @@ const (
) )
// PeekProtocol - reads the first bytes, then checks if it is similar // PeekProtocol - reads the first bytes, then checks if it is similar
// to one of the default http methods // to one of the default http methods. Returns error if there are any
func (c *ConnMux) PeekProtocol() string { // errors in peeking over the connection.
func (c *ConnMux) PeekProtocol() (string, error) {
// Peek for HTTP verbs.
buf, err := c.bufrw.Peek(maxHTTPVerbLen) buf, err := c.bufrw.Peek(maxHTTPVerbLen)
if err != nil { if err != nil {
if err != io.EOF { return "", err
errorIf(err, "Unable to peek into the protocol")
}
return protocolHTTP1
} }
for _, m := range defaultHTTP1Methods {
// Check for HTTP2 methods first.
for _, m := range defaultHTTP2Methods {
if strings.HasPrefix(string(buf), m) { if strings.HasPrefix(string(buf), m) {
return protocolHTTP1 return protocolHTTP2, nil
} }
} }
for _, m := range defaultHTTP2Methods {
// Check for HTTP1 methods.
for _, m := range defaultHTTP1Methods {
if strings.HasPrefix(string(buf), m) { if strings.HasPrefix(string(buf), m) {
return protocolHTTP2 return protocolHTTP1, nil
} }
} }
return protocolTLS
// Default to TLS, this is not a real indication
// that the connection is TLS but that will be
// validated later by doing a handshake.
return protocolTLS, nil
} }
// Read - streams the ConnMux buffer when reset flag is activated, otherwise // Read - streams the ConnMux buffer when reset flag is activated, otherwise
@ -165,8 +173,8 @@ type ListenerMuxAcceptRes struct {
// $ cat /proc/sys/net/ipv4/tcp_keepalive_probes // $ cat /proc/sys/net/ipv4/tcp_keepalive_probes
// 9 // 9
// //
// Effective value of total keep alive comes upto 9 x 3 * time.Minute = 27 Minutes. // Effective value of total keep alive comes upto 9 x 10 * time.Second = 1.5 Minutes.
var defaultKeepAliveTimeout = 3 * time.Minute // 3 minutes. var defaultKeepAliveTimeout = 10 * time.Second // 10 seconds.
// newListenerMux listens and wraps accepted connections with tls after protocol peeking // newListenerMux listens and wraps accepted connections with tls after protocol peeking
func newListenerMux(listener net.Listener, config *tls.Config) *ListenerMux { func newListenerMux(listener net.Listener, config *tls.Config) *ListenerMux {
@ -198,20 +206,42 @@ func newListenerMux(listener net.Listener, config *tls.Config) *ListenerMux {
conn.SetKeepAlive(true) conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(defaultKeepAliveTimeout) conn.SetKeepAlivePeriod(defaultKeepAliveTimeout)
// Allocate new conn muxer.
connMux := NewConnMux(conn)
// Wrap the connection with ConnMux to be able to peek the data in the incoming connection // Wrap the connection with ConnMux to be able to peek the data in the incoming connection
// and decide if we need to wrap the connection itself with a TLS or not // and decide if we need to wrap the connection itself with a TLS or not
go func(conn net.Conn) { go func(connMux *ConnMux) {
connMux := NewConnMux(conn) protocol, err := connMux.PeekProtocol()
if connMux.PeekProtocol() == protocolTLS { if err != nil {
// io.EOF is usually returned by non-http clients,
// just close the connection to avoid any leak.
if err != io.EOF {
errorIf(err, "Unable to peek into incoming protocol")
}
connMux.Close()
return
}
switch protocol {
case protocolTLS:
tlsConn := tls.Server(connMux, l.config)
// Make sure to handshake so that we know that this
// is a TLS connection, if not we should close and reject
// such a connection.
if err = tlsConn.Handshake(); err != nil {
errorIf(err, "TLS handshake failed")
tlsConn.Close()
return
}
l.acceptResCh <- ListenerMuxAcceptRes{ l.acceptResCh <- ListenerMuxAcceptRes{
conn: tls.Server(connMux, l.config), conn: tlsConn,
} }
} else { default:
l.acceptResCh <- ListenerMuxAcceptRes{ l.acceptResCh <- ListenerMuxAcceptRes{
conn: connMux, conn: connMux,
} }
} }
}(conn) }(connMux)
} }
}() }()
return &l return &l
@ -284,10 +314,7 @@ type ServerMux struct {
func NewServerMux(addr string, handler http.Handler) *ServerMux { func NewServerMux(addr string, handler http.Handler) *ServerMux {
m := &ServerMux{ m := &ServerMux{
Server: &http.Server{ Server: &http.Server{
Addr: addr, Addr: addr,
// Do not add any timeouts Golang net.Conn
// closes connections right after 10mins even
// if they are not idle.
Handler: handler, Handler: handler,
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
}, },
@ -349,7 +376,28 @@ func (m *ServerMux) ListenAndServe(certFile, keyFile string) (err error) {
tlsEnabled := certFile != "" && keyFile != "" tlsEnabled := certFile != "" && keyFile != ""
config := &tls.Config{} // Always instantiate. config := &tls.Config{
// Causes servers to use Go's default ciphersuite preferences,
// which are tuned to avoid attacks. Does nothing on clients.
PreferServerCipherSuites: true,
// Only use curves which have assembly implementations
CurvePreferences: []tls.CurveID{
tls.CurveP256,
},
// Set minimum version to TLS 1.2
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// Best disabled, as they don't provide Forward Secrecy,
// but might be necessary for some clients
// tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
// tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
},
} // Always instantiate.
if tlsEnabled { if tlsEnabled {
// Configure TLS in the server // Configure TLS in the server

Loading…
Cancel
Save