diff --git a/cmd/rqlited/main.go b/cmd/rqlited/main.go index cebae470..41cc3277 100644 --- a/cmd/rqlited/main.go +++ b/cmd/rqlited/main.go @@ -3,10 +3,8 @@ package main import ( "crypto/tls" - "crypto/x509" "fmt" "io" - "io/ioutil" "log" "net" "os" @@ -27,6 +25,7 @@ import ( "github.com/rqlite/rqlite/db" "github.com/rqlite/rqlite/disco" httpd "github.com/rqlite/rqlite/http" + "github.com/rqlite/rqlite/rtls" "github.com/rqlite/rqlite/store" "github.com/rqlite/rqlite/tcp" ) @@ -130,28 +129,10 @@ func main() { // Register remaining status providers. httpServ.RegisterStatus("cluster", clstr) - tlsConfig := tls.Config{InsecureSkipVerify: cfg.NoHTTPVerify} - if cfg.X509CACert != "" { - asn1Data, err := ioutil.ReadFile(cfg.X509CACert) - if err != nil { - log.Fatalf("ioutil.ReadFile failed: %s", err.Error()) - } - tlsConfig.RootCAs = x509.NewCertPool() - ok := tlsConfig.RootCAs.AppendCertsFromPEM(asn1Data) - if !ok { - log.Fatalf("failed to parse root CA certificate(s) in %q", cfg.X509CACert) - } - } - if cfg.X509CertClient != "" { - asn1Data, err := ioutil.ReadFile(cfg.X509CertClient) - if err != nil { - log.Fatalf("ioutil.ReadFile failed: %s", err.Error()) - } - tlsConfig.Certificates = make([]tls.Certificate, 1) - tlsConfig.Certificates[0], err = tls.X509KeyPair(asn1Data, []byte(cfg.X509KeyClient)) - if err != nil { - log.Fatalf("tls.X509KeyPair failed: %s", err.Error()) - } + tlsConfig, err := rtls.CreateClientConfig(cfg.X509CertClient, cfg.X509KeyClient, cfg.X509CACert, + cfg.NoHTTPVerify, cfg.TLS1011) + if err != nil { + log.Fatalf("failed to create TLS client config for cluster: %s", err.Error()) } // Create the cluster! @@ -159,7 +140,7 @@ func main() { if err != nil { log.Fatalf("failed to get nodes %s", err.Error()) } - if err := createCluster(cfg, &tlsConfig, len(nodes) > 0, str, httpServ, credStr); err != nil { + if err := createCluster(cfg, tlsConfig, len(nodes) > 0, str, httpServ, credStr); err != nil { log.Fatalf("clustering failure: %s", err.Error()) } diff --git a/http/service.go b/http/service.go index 3ec80588..246af0ff 100644 --- a/http/service.go +++ b/http/service.go @@ -5,7 +5,6 @@ package http import ( "context" "crypto/tls" - "crypto/x509" "encoding/json" "errors" "expvar" @@ -27,6 +26,7 @@ import ( "github.com/rqlite/rqlite/command" "github.com/rqlite/rqlite/command/encoding" "github.com/rqlite/rqlite/queue" + "github.com/rqlite/rqlite/rtls" "github.com/rqlite/rqlite/store" ) @@ -274,6 +274,7 @@ type Service struct { CertFile string // Path to server's own x509 certificate. KeyFile string // Path to server's own x509 private key. TLS1011 bool // Whether older, deprecated TLS should be supported. + ClientVerify bool // Whether client certificates should be verified. DefaultQueueCap int DefaultQueueBatchSz int @@ -324,7 +325,7 @@ func (s *Service) Start() error { return err } } else { - config, err := createTLSConfig(s.CertFile, s.KeyFile, s.ClientCACertFile, s.TLS1011) + config, err := rtls.CreateServerConfig(s.CertFile, s.KeyFile, s.ClientCACertFile, s.ClientVerify, s.TLS1011) if err != nil { return err } @@ -1737,39 +1738,6 @@ func requestQueries(r *http.Request) ([]*command.Statement, error) { return ParseRequest(b) } -// createTLSConfig returns a TLS config from the given cert and key. -func createTLSConfig(certFile, keyFile, clientCACertFile string, tls1011 bool) (*tls.Config, error) { - var err error - - var minTLS = uint16(tls.VersionTLS12) - if tls1011 { - minTLS = tls.VersionTLS10 - } - - config := &tls.Config{ - NextProtos: []string{"h2", "http/1.1"}, - MinVersion: minTLS, - } - config.Certificates = make([]tls.Certificate, 1) - config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, err - } - if clientCACertFile != "" { - asn1Data, err := ioutil.ReadFile(clientCACertFile) - if err != nil { - return nil, err - } - config.ClientCAs = x509.NewCertPool() - ok := config.ClientCAs.AppendCertsFromPEM(asn1Data) - if !ok { - return nil, fmt.Errorf("failed to load CA certificate(s) for client verification in %q", clientCACertFile) - } - config.ClientAuth = tls.RequireAndVerifyClientCert - } - return config, nil -} - // queryParam returns whether the given query param is present. func queryParam(req *http.Request, param string) (bool, error) { err := req.ParseForm() diff --git a/tcp/dialer_test.go b/tcp/dialer_test.go index 9a6d95e7..244cdebc 100644 --- a/tcp/dialer_test.go +++ b/tcp/dialer_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/rqlite/rqlite/rtls" "github.com/rqlite/rqlite/testdata/x509" ) @@ -149,7 +150,7 @@ func mustNewEchoServerTLS() (*echoServer, string, string) { cert := x509.CertFile("") key := x509.KeyFile("") - tlsConfig, err := createTLSConfig(cert, key, "") + tlsConfig, err := rtls.CreateServerConfig(cert, key, "", true, false) if err != nil { panic("failed to create TLS config") } diff --git a/tcp/mux.go b/tcp/mux.go index cf0879d3..35d5da99 100644 --- a/tcp/mux.go +++ b/tcp/mux.go @@ -2,18 +2,18 @@ package tcp import ( "crypto/tls" - "crypto/x509" "errors" "expvar" "fmt" "io" - "io/ioutil" "log" "net" "os" "strconv" "sync" "time" + + "github.com/rqlite/rqlite/rtls" ) const ( @@ -115,13 +115,13 @@ func NewMux(ln net.Listener, adv net.Addr) (*Mux, error) { // NewTLSMux returns a new instance of Mux for ln, and encrypts all traffic // using TLS. If adv is nil, then the addr of ln is used. -func NewTLSMux(ln net.Listener, adv net.Addr, cert, key, caCert string) (*Mux, error) { +func NewTLSMux(ln net.Listener, adv net.Addr, cert, key, caCert string, insecure bool) (*Mux, error) { mux, err := NewMux(ln, adv) if err != nil { return nil, err } - mux.tlsConfig, err = createTLSConfig(cert, key, caCert) + mux.tlsConfig, err = rtls.CreateServerConfig(cert, key, caCert, insecure, false) if err != nil { return nil, err } @@ -270,40 +270,3 @@ func (ln *listener) Close() error { return nil } // Addr always returns nil func (ln *listener) Addr() net.Addr { return nil } - -// newTLSListener returns a net listener which encrypts the traffic using TLS. -func newTLSListener(ln net.Listener, certFile, keyFile, caCertFile string) (net.Listener, error) { - config, err := createTLSConfig(certFile, keyFile, caCertFile) - if err != nil { - return nil, err - } - - return tls.NewListener(ln, config), nil -} - -// createTLSConfig returns a TLS config from the given cert, key and optionally -// Certificate Authority cert for verifying client certificates. -func createTLSConfig(certFile, keyFile, caCertFile string) (*tls.Config, error) { - var err error - config := &tls.Config{} - config.Certificates = make([]tls.Certificate, 1) - config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, err - } - - if caCertFile != "" { - asn1Data, err := ioutil.ReadFile(caCertFile) - if err != nil { - return nil, err - } - config.ClientCAs = x509.NewCertPool() - ok := config.ClientCAs.AppendCertsFromPEM(asn1Data) - if !ok { - return nil, fmt.Errorf("failed to parse Client Auth CA certificate in %q", caCertFile) - } - config.ClientAuth = tls.RequireAndVerifyClientCert - } - - return config, nil -} diff --git a/tcp/mux_test.go b/tcp/mux_test.go index d821470a..7b9d5e05 100644 --- a/tcp/mux_test.go +++ b/tcp/mux_test.go @@ -177,7 +177,7 @@ func TestTLSMux(t *testing.T) { key := x509.KeyFile("") defer os.Remove(key) - mux, err := NewTLSMux(tcpListener, nil, cert, key, "") + mux, err := NewTLSMux(tcpListener, nil, cert, key, "", true) if err != nil { t.Fatalf("failed to create mux: %s", err.Error()) } @@ -200,7 +200,7 @@ func TestTLSMux(t *testing.T) { func TestTLSMux_Fail(t *testing.T) { tcpListener := mustTCPListener("127.0.0.1:0") defer tcpListener.Close() - _, err := NewTLSMux(tcpListener, nil, "xxxx", "yyyy", "") + _, err := NewTLSMux(tcpListener, nil, "xxxx", "yyyy", "", true) if err == nil { t.Fatalf("created mux unexpectedly with bad resources") }