lib/httpserver: move the code, which creates tls.Config, into lib/netutil/tls.go

This syncs the corresponding code with cluster branch
This commit is contained in:
Aliaksandr Valialkin 2022-04-16 15:51:34 +03:00
parent 7e4bdf31ba
commit 7810375c5f
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
8 changed files with 98 additions and 61 deletions

View file

@ -91,44 +91,17 @@ func Serve(addr string, rh RequestHandler) {
} }
logger.Infof("starting http server at %s://%s/", scheme, hostAddr) logger.Infof("starting http server at %s://%s/", scheme, hostAddr)
logger.Infof("pprof handlers are exposed at %s://%s/debug/pprof/", scheme, hostAddr) logger.Infof("pprof handlers are exposed at %s://%s/debug/pprof/", scheme, hostAddr)
lnTmp, err := netutil.NewTCPListener(scheme, addr) var tlsConfig *tls.Config
if err != nil {
logger.Fatalf("cannot start http server at %s: %s", addr, err)
}
ln := net.Listener(lnTmp)
if *tlsEnable { if *tlsEnable {
var certLock sync.Mutex tc, err := netutil.GetServerTLSConfig("", *tlsCertFile, *tlsKeyFile, *tlsCipherSuites)
var certDeadline uint64
var cert *tls.Certificate
c, err := tls.LoadX509KeyPair(*tlsCertFile, *tlsKeyFile)
if err != nil { if err != nil {
logger.Fatalf("cannot load TLS cert from -tlsCertFile=%q, -tlsKeyFile=%q: %s", *tlsCertFile, *tlsKeyFile, err) logger.Fatalf("cannot load TLS cert from -tlsCertFile=%q, -tlsKeyFile=%q: %s", *tlsCertFile, *tlsKeyFile, err)
} }
cipherSuites, err := cipherSuitesFromNames(*tlsCipherSuites) tlsConfig = tc
if err != nil { }
logger.Fatalf("cannot use TLS cipher suites from -tlsCipherSuites=%q: %s", *tlsCipherSuites, err) ln, err := netutil.NewTCPListener(scheme, addr, tlsConfig)
} if err != nil {
cert = &c logger.Fatalf("cannot start http server at %s: %s", addr, err)
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
certLock.Lock()
defer certLock.Unlock()
if fasttime.UnixTimestamp() > certDeadline {
c, err = tls.LoadX509KeyPair(*tlsCertFile, *tlsKeyFile)
if err != nil {
return nil, fmt.Errorf("cannot load TLS cert from -tlsCertFile=%q, -tlsKeyFile=%q: %w", *tlsCertFile, *tlsKeyFile, err)
}
certDeadline = fasttime.UnixTimestamp() + 1
cert = &c
}
return cert, nil
},
CipherSuites: cipherSuites,
}
ln = tls.NewListener(ln, cfg)
} }
serveWithListener(addr, ln, rh) serveWithListener(addr, ln, rh)
} }
@ -693,23 +666,3 @@ func GetRequestURI(r *http.Request) string {
} }
return requestURI + delimiter + queryArgs return requestURI + delimiter + queryArgs
} }
func cipherSuitesFromNames(cipherSuiteNames []string) ([]uint16, error) {
if len(cipherSuiteNames) == 0 {
return nil, nil
}
css := tls.CipherSuites()
cssMap := make(map[string]uint16, len(css))
for _, cs := range css {
cssMap[strings.ToLower(cs.Name)] = cs.ID
}
cipherSuites := make([]uint16, 0, len(cipherSuiteNames))
for _, name := range cipherSuiteNames {
id, ok := cssMap[strings.ToLower(name)]
if !ok {
return nil, fmt.Errorf("unsupported TLS cipher suite name: %s", name)
}
cipherSuites = append(cipherSuites, id)
}
return cipherSuites, nil
}

View file

@ -40,7 +40,7 @@ type Server struct {
// MustStop must be called on the returned server when it is no longer needed. // MustStop must be called on the returned server when it is no longer needed.
func MustStart(addr string, insertHandler func(r io.Reader) error) *Server { func MustStart(addr string, insertHandler func(r io.Reader) error) *Server {
logger.Infof("starting TCP Graphite server at %q", addr) logger.Infof("starting TCP Graphite server at %q", addr)
lnTCP, err := netutil.NewTCPListener("graphite", addr) lnTCP, err := netutil.NewTCPListener("graphite", addr, nil)
if err != nil { if err != nil {
logger.Fatalf("cannot start TCP Graphite server at %q: %s", addr, err) logger.Fatalf("cannot start TCP Graphite server at %q: %s", addr, err)
} }

View file

@ -40,7 +40,7 @@ type Server struct {
// MustStop must be called on the returned server when it is no longer needed. // MustStop must be called on the returned server when it is no longer needed.
func MustStart(addr string, insertHandler func(r io.Reader) error) *Server { func MustStart(addr string, insertHandler func(r io.Reader) error) *Server {
logger.Infof("starting TCP InfluxDB server at %q", addr) logger.Infof("starting TCP InfluxDB server at %q", addr)
lnTCP, err := netutil.NewTCPListener("influx", addr) lnTCP, err := netutil.NewTCPListener("influx", addr, nil)
if err != nil { if err != nil {
logger.Fatalf("cannot start TCP InfluxDB server at %q: %s", addr, err) logger.Fatalf("cannot start TCP InfluxDB server at %q: %s", addr, err)
} }

View file

@ -43,7 +43,7 @@ type Server struct {
// MustStop must be called on the returned server when it is no longer needed. // MustStop must be called on the returned server when it is no longer needed.
func MustStart(addr string, telnetInsertHandler func(r io.Reader) error, httpInsertHandler func(req *http.Request) error) *Server { func MustStart(addr string, telnetInsertHandler func(r io.Reader) error, httpInsertHandler func(req *http.Request) error) *Server {
logger.Infof("starting TCP OpenTSDB collector at %q", addr) logger.Infof("starting TCP OpenTSDB collector at %q", addr)
lnTCP, err := netutil.NewTCPListener("opentsdb", addr) lnTCP, err := netutil.NewTCPListener("opentsdb", addr, nil)
if err != nil { if err != nil {
logger.Fatalf("cannot start TCP OpenTSDB collector at %q: %s", addr, err) logger.Fatalf("cannot start TCP OpenTSDB collector at %q: %s", addr, err)
} }

View file

@ -30,7 +30,7 @@ type Server struct {
// MustStop must be called on the returned server when it is no longer needed. // MustStop must be called on the returned server when it is no longer needed.
func MustStart(addr string, insertHandler func(r *http.Request) error) *Server { func MustStart(addr string, insertHandler func(r *http.Request) error) *Server {
logger.Infof("starting HTTP OpenTSDB server at %q", addr) logger.Infof("starting HTTP OpenTSDB server at %q", addr)
lnTCP, err := netutil.NewTCPListener("opentsdbhttp", addr) lnTCP, err := netutil.NewTCPListener("opentsdbhttp", addr, nil)
if err != nil { if err != nil {
logger.Fatalf("cannot start HTTP OpenTSDB collector at %q: %s", addr, err) logger.Fatalf("cannot start HTTP OpenTSDB collector at %q: %s", addr, err)
} }

View file

@ -1,6 +1,7 @@
package netutil package netutil
import ( import (
"crypto/tls"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@ -13,16 +14,19 @@ import (
var enableTCP6 = flag.Bool("enableTCP6", false, "Whether to enable IPv6 for listening and dialing. By default only IPv4 TCP and UDP is used") var enableTCP6 = flag.Bool("enableTCP6", false, "Whether to enable IPv6 for listening and dialing. By default only IPv4 TCP and UDP is used")
// NewTCPListener returns new TCP listener for the given addr. // NewTCPListener returns new TCP listener for the given addr and optional tlsConfig.
// //
// name is used for exported metrics. Each listener in the program must have // name is used for exported metrics. Each listener in the program must have
// distinct name. // distinct name.
func NewTCPListener(name, addr string) (*TCPListener, error) { func NewTCPListener(name, addr string, tlsConfig *tls.Config) (*TCPListener, error) {
network := GetTCPNetwork() network := GetTCPNetwork()
ln, err := net.Listen(network, addr) ln, err := net.Listen(network, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if tlsConfig != nil {
ln = tls.NewListener(ln, tlsConfig)
}
tln := &TCPListener{ tln := &TCPListener{
Listener: ln, Listener: ln,

80
lib/netutil/tls.go Normal file
View file

@ -0,0 +1,80 @@
package netutil
import (
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
"sync"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
)
// GetServerTLSConfig returns TLS config for the server with possible client verification (mTLS) if tlsCAFile isn't empty.
func GetServerTLSConfig(tlsCAFile, tlsCertFile, tlsKeyFile string, tlsCipherSuites []string) (*tls.Config, error) {
var certLock sync.Mutex
var certDeadline uint64
var cert *tls.Certificate
c, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
if err != nil {
return nil, fmt.Errorf("cannot load TLS cert from certFile=%q, keyFile=%q: %w", tlsCertFile, tlsKeyFile, err)
}
cipherSuites, err := cipherSuitesFromNames(tlsCipherSuites)
if err != nil {
return nil, fmt.Errorf("cannot use TLS cipher suites from tlsCipherSuites=%q: %w", tlsCipherSuites, err)
}
cert = &c
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
certLock.Lock()
defer certLock.Unlock()
if fasttime.UnixTimestamp() > certDeadline {
c, err = tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
if err != nil {
return nil, fmt.Errorf("cannot load TLS cert from certFile=%q, keyFile=%q: %w", tlsCertFile, tlsKeyFile, err)
}
certDeadline = fasttime.UnixTimestamp() + 1
cert = &c
}
return cert, nil
},
CipherSuites: cipherSuites,
}
if tlsCAFile != "" {
// Enable mTLS ( https://en.wikipedia.org/wiki/Mutual_authentication#mTLS )
cfg.ClientAuth = tls.RequireAndVerifyClientCert
cp := x509.NewCertPool()
caPEM, err := fs.ReadFileOrHTTP(tlsCAFile)
if err != nil {
return nil, fmt.Errorf("cannot read tlsCAFile=%q: %w", tlsCAFile, err)
}
if !cp.AppendCertsFromPEM(caPEM) {
return nil, fmt.Errorf("cannot parse data for tlsCAFile=%q: %s", tlsCAFile, caPEM)
}
cfg.ClientCAs = cp
}
return cfg, nil
}
func cipherSuitesFromNames(cipherSuiteNames []string) ([]uint16, error) {
if len(cipherSuiteNames) == 0 {
return nil, nil
}
css := tls.CipherSuites()
cssMap := make(map[string]uint16, len(css))
for _, cs := range css {
cssMap[strings.ToLower(cs.Name)] = cs.ID
}
cipherSuites := make([]uint16, 0, len(cipherSuiteNames))
for _, name := range cipherSuiteNames {
id, ok := cssMap[strings.ToLower(name)]
if !ok {
return nil, fmt.Errorf("unsupported TLS cipher suite name: %s", name)
}
cipherSuites = append(cipherSuites, id)
}
return cipherSuites, nil
}

View file

@ -1,4 +1,4 @@
package httpserver package netutil
import ( import (
"reflect" "reflect"