From fb0cec5591373e8d333aa9fcc6844ad19030f047 Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Fri, 16 Aug 2019 16:06:54 +0100 Subject: [PATCH] Backend: Adds support for HTTP/2 (#18358) * Backend: Adds support for HTTP/2 * Adds mozilla recommended ciphers * Updates sample.ini and config documentation --- conf/defaults.ini | 2 +- conf/sample.ini | 2 +- docs/sources/installation/configuration.md | 6 +-- pkg/api/http_server.go | 45 ++++++++++++++++++++++ pkg/middleware/middleware.go | 2 +- pkg/setting/setting.go | 6 +++ 6 files changed, 57 insertions(+), 6 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 94daef9c4cc..3a94c17e19a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -28,7 +28,7 @@ provisioning = conf/provisioning #################################### Server ############################## [server] -# Protocol (http, https, socket) +# Protocol (http, https, h2, socket) protocol = http # The ip address to bind to, empty will bind to all interfaces diff --git a/conf/sample.ini b/conf/sample.ini index 8b7d5e7bcf8..5029b541638 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -28,7 +28,7 @@ #################################### Server #################################### [server] -# Protocol (http, https, socket) +# Protocol (http, https, h2, socket) ;protocol = http # The ip address to bind to, empty will bind to all interfaces diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index ab926048233..3c4eceaaab6 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -127,7 +127,7 @@ Another way is put a webserver like Nginx or Apache in front of Grafana and have ### protocol -`http`,`https` or `socket` +`http`,`https`,`h2` or `socket` > **Note** Grafana versions earlier than 3.0 are vulnerable to [POODLE](https://en.wikipedia.org/wiki/POODLE). So we strongly recommend to upgrade to 3.x or use a reverse proxy for ssl termination. @@ -179,11 +179,11 @@ reasons. ### cert_file -Path to the certificate file (if `protocol` is set to `https`). +Path to the certificate file (if `protocol` is set to `https` or `h2`). ### cert_key -Path to the certificate key file (if `protocol` is set to `https`). +Path to the certificate key file (if `protocol` is set to `https` or `h2`). ### router_logging diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 68e8c5bb9fa..213f4314eef 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -110,6 +110,12 @@ func (hs *HTTPServer) Run(ctx context.Context) error { hs.log.Debug("server was shutdown gracefully") return nil } + case setting.HTTP2: + err = hs.listenAndServeH2TLS(setting.CertFile, setting.KeyFile) + if err == http.ErrServerClosed { + hs.log.Debug("server was shutdown gracefully") + return nil + } case setting.HTTPS: err = hs.listenAndServeTLS(setting.CertFile, setting.KeyFile) if err == http.ErrServerClosed { @@ -181,6 +187,45 @@ func (hs *HTTPServer) listenAndServeTLS(certfile, keyfile string) error { return hs.httpSrv.ListenAndServeTLS(setting.CertFile, setting.KeyFile) } +func (hs *HTTPServer) listenAndServeH2TLS(certfile, keyfile string) error { + if certfile == "" { + return fmt.Errorf("cert_file cannot be empty when using HTTP2") + } + + if keyfile == "" { + return fmt.Errorf("cert_key cannot be empty when using HTTP2") + } + + if _, err := os.Stat(setting.CertFile); os.IsNotExist(err) { + return fmt.Errorf(`Cannot find SSL cert_file at %v`, setting.CertFile) + } + + if _, err := os.Stat(setting.KeyFile); os.IsNotExist(err) { + return fmt.Errorf(`Cannot find SSL key_file at %v`, setting.KeyFile) + } + + tlsCfg := &tls.Config{ + MinVersion: tls.VersionTLS12, + PreferServerCipherSuites: false, + CipherSuites: []uint16{ + tls.TLS_CHACHA20_POLY1305_SHA256, + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + }, + NextProtos: []string{"h2", "http/1.1"}, + } + + hs.httpSrv.TLSConfig = tlsCfg + + return hs.httpSrv.ListenAndServeTLS(setting.CertFile, setting.KeyFile) +} + func (hs *HTTPServer) newMacaron() *macaron.Macaron { macaron.Env = setting.Env m := macaron.New() diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 021ca846c57..41b11025063 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -282,7 +282,7 @@ func AddDefaultResponseHeaders() macaron.Handler { // AddSecurityHeaders adds various HTTP(S) response headers that enable various security protections behaviors in the client's browser. func AddSecurityHeaders(w macaron.ResponseWriter) { - if setting.Protocol == setting.HTTPS && setting.StrictTransportSecurity { + if (setting.Protocol == setting.HTTPS || setting.Protocol == setting.HTTP2) && setting.StrictTransportSecurity { strictHeaderValues := []string{fmt.Sprintf("max-age=%v", setting.StrictTransportSecurityMaxAge)} if setting.StrictTransportSecurityPreload { strictHeaderValues = append(strictHeaderValues, "preload") diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index f554818bc33..d5f62ef8893 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -29,6 +29,7 @@ type Scheme string const ( HTTP Scheme = "http" HTTPS Scheme = "https" + HTTP2 Scheme = "h2" SOCKET Scheme = "socket" DEFAULT_HTTP_ADDR string = "0.0.0.0" ) @@ -639,6 +640,11 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { CertFile = server.Key("cert_file").String() KeyFile = server.Key("cert_key").String() } + if protocolStr == "h2" { + Protocol = HTTP2 + CertFile = server.Key("cert_file").String() + KeyFile = server.Key("cert_key").String() + } if protocolStr == "socket" { Protocol = SOCKET SocketPath = server.Key("socket").String()