diff --git a/TODO.md b/TODO.md index 68c70b53..9f9f1d7a 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,7 @@ # General - [ ] SSL/TLS support +- [ ] Better user/pass support using bcrypt etc. - [ ] Pedantic state - [ ] brew, apt-get, rpm, chocately (windows) - [ ] Dynamic socket buffer sizes diff --git a/server/client.go b/server/client.go index 10a20d45..37d26c26 100644 --- a/server/client.go +++ b/server/client.go @@ -220,31 +220,7 @@ func (c *client) traceOp(format, op string, arg []byte) { c.Tracef(format, opa) } -// Process the info message if we are a route. -func (c *client) processRouteInfo(info *Info) { - c.mu.Lock() - if c.route == nil { - c.mu.Unlock() - return - } - c.route.remoteID = info.ID - - // Check to see if we have this remote already registered. - // This can happen when both servers have routes to each other. - s := c.srv - c.mu.Unlock() - - if s.addRoute(c) { - c.Debugf("Registering remote route %q", info.ID) - // Send our local subscriptions to this route. - s.sendLocalSubsToRoute(c) - } else { - c.Debugf("Detected duplicate remote route %q", info.ID) - c.closeConnection() - } -} - -// Process the information messages from Clients and other routes. +// Process the information messages from Clients and other Routes. func (c *client) processInfo(arg []byte) error { info := Info{} if err := json.Unmarshal(arg, &info); err != nil { @@ -293,6 +269,7 @@ func (c *client) processConnect(arg []byte) error { func (c *client) authTimeout() { c.sendErr("Authorization Timeout") + c.Debugf("Authorization Timeout") c.closeConnection() } diff --git a/server/client_test.go b/server/client_test.go index 4a787f19..19bcb845 100644 --- a/server/client_test.go +++ b/server/client_test.go @@ -18,7 +18,7 @@ type serverInfo struct { Port uint `json:"port"` Version string `json:"version"` AuthRequired bool `json:"auth_required"` - SslRequired bool `json:"ssl_required"` + TLSRequired bool `json:"ssl_required"` MaxPayload int64 `json:"max_payload"` } @@ -93,7 +93,7 @@ func TestClientCreateAndInfo(t *testing.T) { } // Sanity checks if info.MaxPayload != MAX_PAYLOAD_SIZE || - info.AuthRequired || info.SslRequired || + info.AuthRequired || info.TLSRequired || info.Port != DEFAULT_PORT { t.Fatalf("INFO inconsistent: %+v\n", info) } diff --git a/server/configs/certs/key.pem b/server/configs/certs/key.pem new file mode 100644 index 00000000..240baa40 --- /dev/null +++ b/server/configs/certs/key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/server/configs/certs/server.pem b/server/configs/certs/server.pem new file mode 100644 index 00000000..8bf518d6 --- /dev/null +++ b/server/configs/certs/server.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgIJAMBAkt0mj5R8MA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUx +NzE3MjZaFw0xOTExMDQxNzE3MjZaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKM5pTCAWQK9DptePVucoWeVAqY8 +Pr4ldaHoR4Zm+dUa57OffYu0fxK8Vwyxp3tLhWBWQ6etfR++8BxuhB7OHpIJ330Y +7VNJJXq5Uz0T0acfW5kS/8jqprZQDg1NeYUjGAopPy7hfI65ueC/mVBrbREbyNSM ++c22BM8hLjTNElvHVsc3340DJMTxBqEofjSjsiulzrYqDbpPLNTMSO8mpmETKkVC +mKD8HI8U0i4MgJ/AwUKzVaRAKKlTdUqAQrydK0FEogCNtPe4Ds7kanlqWvoPsD64 +vnVyXkKE77xUFcaOShV0Oh3itcVel6mllnj9f7AvgwKX5t0AqqTz6EEci/grnZfr +4ARaPAjvW4OxweKwoYnu8pbeVVHYY4XJQYhJGqJOHwtQ5ayyLs/lKOE0NuyvK5y1 +6gDghmwG+WJc3q6BHrEslDKFRTTc74ZgWxQfXZ1N2aQ1Ci/MNJ/xNgo3vpvO5Eik +sGvF3czJSaG6iJtZ/8Kpb6VddGio1TTwKs9CAqJ+PuKBe8B0n3gWcnwICtvGcqs4 +zureLRFP4rnM6+PR2LJODijPc/JmoEy/rLbHoJZg/EuxKuaq/FFjpZS/xjtHtcxc +KxE8/vVq9FZWQlJC/lG6GLr7eJ6PMnEM+1YXFWCXU9GY2fgNEzkP7CRk3WMtmUby +15NS42g1KjQ2pqCfAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA +ATANBgkqhkiG9w0BAQsFAAOCAgEAC7q54dnr9ESCQZrVn+47hZcfweukjJAjgTr6 +7lj2yM6AAe2JaK6kds8uXfluxkJQxtLz7PdA0DDLDlAphx3Qx1ss86mcRuhNZWob +GAsKH4GM0y+d2U7ar8GpQRaJXdgRGlKOxgEZ2pyagY4I4dwn/0M35ccHaySz2Xx2 +zfNcIIt8Z1B29BmdNGfI+EfUTFkfyqovjh4mFuLEFsCNyluYKYagN4A7P5NEJpUF +/8wf9c6suJCzIjtBkWLOs95syDy1vw92x29vDQClArPksM6G4ReLYUXoygT9y2SI +URPRYYVjGupDcXD989yVIYNYkert6Ib3Cf2wlvvgXe4c3QnT3Rm7jg2RvR7B73Fc +j4EqnOGvI5XGQPFHYBzSJPs9sVVP9b8c7G/SpMTCdd3hK5idOkBAS3WOecnvE23t +JSHQvdegenEFL0yXYe6Rhag1Bj9Q01QizwCBDwoH3Pfvi5ZAHEXW253n6bD3p6OK +NuzoCzSFZBfrzFP4V/2VUtUYKudQ3bJMKKP2snvPyphG/UmGGfZUXb/kVA19Mpd4 +TMIaZD7dgo3toXXygPyWzyblRcvMY2UUWM5n43f6JEovFfxEvagErbAbJvCzR1XW +N441LebEnCrYf8XslEsulKd7nGZioi31M4971rtoawpD29HBAlNWHKBR2k4hh90F +laaNJWY= +-----END CERTIFICATE----- diff --git a/server/configs/tls.conf b/server/configs/tls.conf new file mode 100644 index 00000000..54fe8788 --- /dev/null +++ b/server/configs/tls.conf @@ -0,0 +1,16 @@ + +# Simple TLS config file + +port: 4443 +net: apcera.me # net interface + +tls { + cert_file: "./configs/certs/server.pem" + key_file: "./configs/certs/key.pem" +} + +authorization { + user: derek + password: buckley + timeout: 1 +} diff --git a/server/opts.go b/server/opts.go index d47b066d..747dd1ba 100644 --- a/server/opts.go +++ b/server/opts.go @@ -1,8 +1,10 @@ -// Copyright 2012-2013 Apcera Inc. All rights reserved. +// Copyright 2012-2015 Apcera Inc. All rights reserved. package server import ( + "crypto/tls" + "crypto/x509" "fmt" "io/ioutil" "net" @@ -30,7 +32,6 @@ type Options struct { PingInterval time.Duration `json:"ping_interval"` MaxPingsOut int `json:"ping_max"` HTTPPort int `json:"http_port"` - SslTimeout float64 `json:"ssl_timeout"` AuthTimeout float64 `json:"auth_timeout"` MaxControlLine int `json:"max_control_line"` MaxPayload int `json:"max_payload"` @@ -40,6 +41,7 @@ type Options struct { ClusterUsername string `json:"-"` ClusterPassword string `json:"-"` ClusterAuthTimeout float64 `json:"auth_timeout"` + ClusterTLSConfig *tls.Config `json:"-"` ProfPort int `json:"-"` PidFile string `json:"-"` LogFile string `json:"-"` @@ -48,6 +50,8 @@ type Options struct { Routes []*url.URL `json:"-"` RoutesStr string `json:"-"` BufSize int `json:"-"` + TLSTimeout float64 `json:"tls_timeout"` + TLSConfig *tls.Config `json:"-"` } type authorization struct { @@ -56,6 +60,14 @@ type authorization struct { timeout float64 } +// This struct holds the parsed tls config information. +type tlsConfig struct { + certFile string + keyFile string + caFile string + verify bool +} + // ProcessConfigFile processes a configuration file. // FIXME(dlc): Hacky func ProcessConfigFile(configFile string) (*Options, error) { @@ -118,6 +130,11 @@ func ProcessConfigFile(configFile string) (*Options, error) { opts.MaxPending = int(v.(int64)) case "max_connections", "max_conn": opts.MaxConn = int(v.(int64)) + case "tls": + tlsm := v.(map[string]interface{}) + if opts.TLSConfig, err = parseTLS(tlsm); err != nil { + return nil, err + } } } return opts, nil @@ -148,6 +165,12 @@ func parseCluster(cm map[string]interface{}, opts *Options) error { } opts.Routes = append(opts.Routes, url) } + case "tls": + var err error + tlsm := mv.(map[string]interface{}) + if opts.ClusterTLSConfig, err = parseTLS(tlsm); err != nil { + return err + } } } return nil @@ -176,6 +199,84 @@ func parseAuthorization(am map[string]interface{}) authorization { return auth } +// Helper function to parse TLS configs. +func parseTLS(tlsm map[string]interface{}) (*tls.Config, error) { + tc := tlsConfig{} + for mk, mv := range tlsm { + switch strings.ToLower(mk) { + case "cert_file": + certFile, ok := mv.(string) + if !ok { + return nil, fmt.Errorf("error parsing tls config, expected 'cert_file' to be filename") + } + tc.certFile = certFile + case "key_file": + keyFile, ok := mv.(string) + if !ok { + return nil, fmt.Errorf("error parsing tls config, expected 'key_file' to be filename") + } + tc.keyFile = keyFile + case "ca_file": + caFile, ok := mv.(string) + if !ok { + return nil, fmt.Errorf("error parsing tls config, expected 'ca_file' to be filename") + } + tc.caFile = caFile + case "verify": + verify, ok := mv.(bool) + if !ok { + return nil, fmt.Errorf("error parsing tls config, expected 'verify' to be a boolean") + } + tc.verify = verify + + default: + return nil, fmt.Errorf("error parsing tls config, unknown field [%q]", mk) + } + } + // Now load in cert and private key + cert, err := tls.LoadX509KeyPair(tc.certFile, tc.keyFile) + if err != nil { + return nil, fmt.Errorf("error parsing X509 certificate/key pair: %v", err) + } + cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return nil, fmt.Errorf("error parsing certificate: %v", err) + } + // Create TLSConfig + // We will determine the cipher suites that we prefer. + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + PreferServerCipherSuites: true, + MinVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + // The SHA384 versions are only in Go1.5 + // tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + // tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + } + // Require client certificates as needed + if tc.verify == true { + config.ClientAuth = tls.RequireAnyClientCert + } + // Add in CAs if applicable. + if tc.caFile != "" { + rootPEM, err := ioutil.ReadFile(tc.caFile) + if err != nil || rootPEM == nil { + return nil, err + } + pool := x509.NewCertPool() + ok := pool.AppendCertsFromPEM([]byte(rootPEM)) + if !ok { + return nil, fmt.Errorf("failed to parse root ca certificate") + } + config.RootCAs = pool + } + + return &config, nil +} + // MergeOptions will merge two options giving preference to the flagOpts // if the item is present. func MergeOptions(fileOpts, flagOpts *Options) *Options { @@ -349,8 +450,8 @@ func processOptions(opts *Options) { if opts.MaxPingsOut == 0 { opts.MaxPingsOut = DEFAULT_PING_MAX_OUT } - if opts.SslTimeout == 0 { - opts.SslTimeout = float64(SSL_TIMEOUT) / float64(time.Second) + if opts.TLSTimeout == 0 { + opts.TLSTimeout = float64(SSL_TIMEOUT) / float64(time.Second) } if opts.AuthTimeout == 0 { opts.AuthTimeout = float64(AUTH_TIMEOUT) / float64(time.Second) diff --git a/server/opts_test.go b/server/opts_test.go index 821dc51a..a1b0cefc 100644 --- a/server/opts_test.go +++ b/server/opts_test.go @@ -1,8 +1,9 @@ -// Copyright 2013-2014 Apcera Inc. All rights reserved. +// Copyright 2013-2015 Apcera Inc. All rights reserved. package server import ( + "crypto/tls" "net/url" "reflect" "testing" @@ -16,7 +17,7 @@ func TestDefaultOptions(t *testing.T) { MaxConn: DEFAULT_MAX_CONNECTIONS, PingInterval: DEFAULT_PING_INTERVAL, MaxPingsOut: DEFAULT_PING_MAX_OUT, - SslTimeout: float64(SSL_TIMEOUT) / float64(time.Second), + TLSTimeout: float64(SSL_TIMEOUT) / float64(time.Second), AuthTimeout: float64(AUTH_TIMEOUT) / float64(time.Second), MaxControlLine: MAX_CONTROL_LINE_SIZE, MaxPayload: MAX_PAYLOAD_SIZE, @@ -77,6 +78,54 @@ func TestConfigFile(t *testing.T) { } } +func TestTLSConfigFile(t *testing.T) { + golden := &Options{ + Host: "apcera.me", + Port: 4443, + Username: "derek", + Password: "buckley", + AuthTimeout: 1.0, + } + opts, err := ProcessConfigFile("./configs/tls.conf") + if err != nil { + t.Fatalf("Received an error reading config file: %v\n", err) + } + tlsConfig := opts.TLSConfig + if tlsConfig == nil { + t.Fatal("Expected opts.TLSConfig to be non-nil") + } + opts.TLSConfig = nil + if !reflect.DeepEqual(golden, opts) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } + // Now check TLSConfig a bit more closely + // CipherSuites + ciphers := []uint16{ + // tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + // tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + } + if !reflect.DeepEqual(tlsConfig.CipherSuites, ciphers) { + t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CipherSuites) + } + if tlsConfig.MinVersion != tls.VersionTLS12 { + t.Fatalf("Expected MinVersion of 1.2 [%v], got [%v]", tls.VersionTLS12, tlsConfig.MinVersion) + } + if tlsConfig.PreferServerCipherSuites != true { + t.Fatal("Expected PreferServerCipherSuites to be true") + } + // Verify hostname is correct in certificate + if len(tlsConfig.Certificates) != 1 { + t.Fatal("Expected 1 certificate") + } + cert := tlsConfig.Certificates[0].Leaf + if err := cert.VerifyHostname("localhost"); err != nil { + t.Fatalf("Could not verify hostname in certificate: %v\n", err) + } +} + func TestMergeOverrides(t *testing.T) { golden := &Options{ Host: "apcera.me", diff --git a/server/route.go b/server/route.go index 9fc5ffea..71934235 100644 --- a/server/route.go +++ b/server/route.go @@ -1,9 +1,11 @@ -// Copyright 2013-2014 Apcera Inc. All rights reserved. +// Copyright 2013-2015 Apcera Inc. All rights reserved. package server import ( + "bufio" "bytes" + "crypto/tls" "encoding/json" "fmt" "net" @@ -24,14 +26,14 @@ type connectInfo struct { Pedantic bool `json:"pedantic"` User string `json:"user,omitempty"` Pass string `json:"pass,omitempty"` - Ssl bool `json:"ssl_required"` + TLS bool `json:"tls_required"` Name string `json:"name"` } const conProto = "CONNECT %s" + _CRLF_ // Lock should be held entering here. -func (c *client) sendConnect() { +func (c *client) sendConnect(tlsRequired bool) { var user, pass string if userInfo := c.route.url.User; userInfo != nil { user = userInfo.Username() @@ -42,18 +44,43 @@ func (c *client) sendConnect() { Pedantic: false, User: user, Pass: pass, - Ssl: false, + TLS: tlsRequired, Name: c.srv.info.ID, } b, err := json.Marshal(cinfo) if err != nil { Errorf("Error marshalling CONNECT to route: %v\n", err) c.closeConnection() + return } c.bw.WriteString(fmt.Sprintf(conProto, b)) c.bw.Flush() } +// Process the info message if we are a route. +func (c *client) processRouteInfo(info *Info) { + c.mu.Lock() + if c.route == nil { + c.mu.Unlock() + return + } + c.route.remoteID = info.ID + + // Check to see if we have this remote already registered. + // This can happen when both servers have routes to each other. + s := c.srv + c.mu.Unlock() + + if s.addRoute(c) { + c.Debugf("Registering remote route %q", info.ID) + // Send our local subscriptions to this route. + s.sendLocalSubsToRoute(c) + } else { + c.Debugf("Detected duplicate remote route %q", info.ID) + c.closeConnection() + } +} + // This will send local subscription state to a new route connection. // FIXME(dlc) - This could be a DOS or perf issue with many clients // and large subscription space. Plus buffering in place not a good idea. @@ -88,9 +115,11 @@ func (s *Server) createRoute(conn net.Conn, rURL *url.URL) *client { r := &route{didSolicit: didSolicit} c := &client{srv: s, nc: conn, opts: clientOpts{}, typ: ROUTER, route: r} - // Grab JSON info string + // Grab server variables. s.mu.Lock() info := s.routeInfoJSON + authRequired := s.routeInfo.AuthRequired + tlsRequired := s.routeInfo.TLSRequired s.mu.Unlock() // Grab lock @@ -101,18 +130,46 @@ func (s *Server) createRoute(conn net.Conn, rURL *url.URL) *client { c.Debugf("Route connection created") + // Check for TLS + if tlsRequired { + // Copy off the config to add in ServerName if we + tlsConfig := *s.opts.ClusterTLSConfig + + // If we solicited, we will act like the client, otherwise the server. + if didSolicit { + c.Debugf("Starting TLS route client handshake") + // Specify the ServerName we are expecting. + host, _, _ := net.SplitHostPort(rURL.Host) + tlsConfig.ServerName = host + c.nc = tls.Client(c.nc, &tlsConfig) + } else { + c.Debugf("Starting TLS route server handshake") + c.nc = tls.Server(c.nc, &tlsConfig) + } + + conn := c.nc.(*tls.Conn) + err := conn.Handshake() + if err != nil { + c.Debugf("TLS route handshake error: %v", err) + c.closeConnection() + return nil + } + // Rewrap bw + c.bw = bufio.NewWriterSize(c.nc, s.opts.BufSize) + } + // Queue Connect proto if we solicited the connection. if didSolicit { r.url = rURL c.Debugf("Route connect msg sent") - c.sendConnect() + c.sendConnect(tlsRequired) } // Send our info to the other side. s.sendInfo(c, info) // Check for Auth required state for incoming connections. - if s.routeInfo.AuthRequired && !didSolicit { + if authRequired && !didSolicit { ttl := secondsToDuration(s.opts.ClusterAuthTimeout) c.setAuthTimer(ttl) } @@ -295,14 +352,18 @@ func (s *Server) routeAcceptLoop(ch chan struct{}) { // StartRouting will start the accept loop on the cluster host:port // and will actively try to connect to listed routes. func (s *Server) StartRouting() { + // Check for TLSConfig + tlsReq := s.opts.ClusterTLSConfig != nil info := Info{ ID: s.info.ID, Version: s.info.Version, Host: s.opts.ClusterHost, Port: s.opts.ClusterPort, AuthRequired: false, - SslRequired: false, - MaxPayload: MAX_PAYLOAD_SIZE, + TLSRequired: tlsReq, + SSLRequired: tlsReq, + TLSVerify: tlsReq, + MaxPayload: s.info.MaxPayload, } // Check for Auth items if s.opts.ClusterUsername != "" { diff --git a/server/server.go b/server/server.go index 82e50e8d..d5aa685a 100644 --- a/server/server.go +++ b/server/server.go @@ -3,6 +3,8 @@ package server import ( + "bufio" + "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -30,7 +32,9 @@ type Info struct { Host string `json:"host"` Port int `json:"port"` AuthRequired bool `json:"auth_required"` - SslRequired bool `json:"ssl_required"` + SSLRequired bool `json:"ssl_required"` // ssl json used for older clients + TLSRequired bool `json:"tls_required"` + TLSVerify bool `json:"tls_verify"` MaxPayload int `json:"max_payload"` } @@ -73,6 +77,11 @@ type stats struct { // New will setup a new server struct after parsing the options. func New(opts *Options) *Server { processOptions(opts) + + // Process TLS options, including whether we require client certificates. + tlsReq := opts.TLSConfig != nil + verify := (tlsReq == true && opts.TLSConfig.ClientAuth == tls.RequireAnyClientCert) + info := Info{ ID: genID(), Version: VERSION, @@ -80,7 +89,9 @@ func New(opts *Options) *Server { Host: opts.Host, Port: opts.Port, AuthRequired: false, - SslRequired: false, + TLSRequired: tlsReq, + SSLRequired: tlsReq, + TLSVerify: verify, MaxPayload: opts.MaxPayload, } @@ -393,6 +404,7 @@ func (s *Server) createClient(conn net.Conn) *client { s.mu.Lock() info := s.infoJSON authRequired := s.info.AuthRequired + tlsRequired := s.info.TLSRequired s.mu.Unlock() // Grab lock @@ -412,6 +424,19 @@ func (s *Server) createClient(conn net.Conn) *client { // Send our information. s.sendInfo(c, info) + // Check for TLS + if tlsRequired { + c.Debugf("Starting TLS client connection handshake") + c.nc = tls.Server(c.nc, s.opts.TLSConfig) + conn := c.nc.(*tls.Conn) + err := conn.Handshake() + if err != nil { + c.Debugf("TLS handshake error: %v", err) + } + // Rewrap bw + c.bw = bufio.NewWriterSize(c.nc, s.opts.BufSize) + } + // Unlock to register c.mu.Unlock() diff --git a/test/cluster_tls_test.go b/test/cluster_tls_test.go new file mode 100644 index 00000000..3182f771 --- /dev/null +++ b/test/cluster_tls_test.go @@ -0,0 +1,62 @@ +// Copyright 2013-2015 Apcera Inc. All rights reserved. + +package test + +import ( + "testing" + "time" + + "github.com/nats-io/gnatsd/server" +) + +func runTLSServers(t *testing.T) (srvA, srvB *server.Server, optsA, optsB *server.Options) { + srvA, optsA = RunServerWithConfig("./configs/srv_a_tls.conf") + srvB, optsB = RunServerWithConfig("./configs/srv_b_tls.conf") + return +} + +func TestTLSClusterConfig(t *testing.T) { + srvA, srvB, _, _ := runTLSServers(t) + defer srvA.Shutdown() + defer srvB.Shutdown() + + // Wait for the setup + time.Sleep(1 * time.Second) + + if numRoutesA := srvA.NumRoutes(); numRoutesA != 1 { + t.Fatalf("Expected one route for srvA, got %d\n", numRoutesA) + } + if numRoutesB := srvB.NumRoutes(); numRoutesB != 1 { + t.Fatalf("Expected one route for srvB, got %d\n", numRoutesB) + } +} + +func TestBasicTLSClusterPubSub(t *testing.T) { + srvA, srvB, optsA, optsB := runTLSServers(t) + defer srvA.Shutdown() + defer srvB.Shutdown() + + // Wait for the setup + time.Sleep(500 * time.Millisecond) + + clientA := createClientConn(t, optsA.Host, optsA.Port) + defer clientA.Close() + + clientB := createClientConn(t, optsB.Host, optsB.Port) + defer clientB.Close() + + sendA, expectA := setupConn(t, clientA) + sendA("SUB foo 22\r\n") + sendA("PING\r\n") + expectA(pongRe) + + sendB, expectB := setupConn(t, clientB) + sendB("PUB foo 2\r\nok\r\n") + sendB("PING\r\n") + expectB(pongRe) + + expectMsgs := expectMsgsCommand(t, expectA) + + matches := expectMsgs(1) + checkMsg(t, matches[0], "foo", "22", "", "2", "ok") +} diff --git a/test/configs/certs/ca.pem b/test/configs/certs/ca.pem new file mode 100644 index 00000000..17447f94 --- /dev/null +++ b/test/configs/certs/ca.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +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 ++Svp +-----END CERTIFICATE----- diff --git a/test/configs/certs/client-cert.pem b/test/configs/certs/client-cert.pem new file mode 100644 index 00000000..549c9b38 --- /dev/null +++ b/test/configs/certs/client-cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFPDCCAySgAwIBAgIJAO+k4G7bNTypMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy +MzEwNDdaFw0xOTExMDQyMzEwNDdaMBYxFDASBgNVBAMTC25hdHMtY2xpZW50MIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArgLxszD5/vDrDUwwIEgQx9I0 +J/H6MXPO0Tj9D2BnR+nwjCe9M03fsq4Il96BVzoaAiAQD1r4NyAX2adKydlnE3/m +bUFiSVHErJceEi9aSs+WlLdmKEgU2qrsIal9KzthlI786qtjb7OFSCxP14R4xYA5 +dlZXhJ9oUuFhVTdaVmRMzWuWj8RbBx8VptSZ0f7Q+Uv8GuB0kyiVkv6GYcH/IWuI +7jnM0QcVWBmxJfWmqd0yx/FLlX/LRXqdiyoFSIlMaP0VOwto3uEhAoBk83Z+/zrZ +Brymx1Nnz3qzTCf8/mdMjPuWibXDTLbo0/Kf6neHs6wxx8irb1ZfIwhn8grXTcgd +rg9bfcyyUOBey7QXiedpU0xFqoH26E+Aq+CV4R56i1sJKsSYEGu8O69H8zu5dgan +LZRhcCHcZhMe7Nbiu5BcuOW4r3rGDMTLXSugEX91iy5jJaYmRjtPN5imQIJtf+GK +Vq7YLv4MQV6R3xRiZXaocCae1qzIMc4kxCKvZTmxuJsvIUPjNnGumwbjV/a2fLFX +9tMqUKyEmiPtFtqNH/kmkHCQ5FGYIIj3wGuD5yWfK5Tr3iHOdNJoNNPgPBg9tMRw +j3+W8+uyBxc+FUEb8a9m3R4VmAYyiqgzCA0DWZBF1fOYLWfRnwS5OBKiP4OUlUEb +YZUEzfvDbLOwQrb123cCAwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJ +KoZIhvcNAQELBQADggIBACNKPbvaXwl5rRTqFw37Am1r6e+LkUg9dFogSwXDnuT/ +RRZJi5MHsC5MUOkHB28lTmPwkAogs+LBmKrM0Npzk6OPkT/LCgKqpVoz2Tc1nGMI +Jy8jxPYogMmDCOhoEoC7zsWABMLiX5KDAuKommk61w7AwKu4kK198ngwbfF2fzdH +1DUGID7iV4fyPGI+pCU3Ullv51c5xkhqjVy1JYdYc0+s6rFyVTibSABa7PfHE2ML +A+cNFWoKQhugVHQU7qYvuWvnEqZro2T6nmSmpK3oOaUgVnDuY2q4JwiMbZAtuyD7 +8LFwCim49WzgYcfs/BwKlUrTV/QBYurruHWjElZzwA39/ZlbnOjJJ85j/YqxR+4S +fK/KktegyrPJU3fxdl2+77zVlfgzxaQ//58vx5LgXWhl2KeHyakeD0jQFVn1R7GD +bynAlHlSOr+nGkwP2WVqXKf+l/gb/gUEY7bC8fCVRCctkcK+smEl+sIKH3O9JY8l +rBWjOXkMY91ZDh77hfTNni/s2/DGAoNrEft8rgu3/NPxhCTfQH3ranCryth9mF6I +qsOFr5/81WGKqU+Kec8st/RSU2vBjBp41HILAEEhUiB6prhc9B3+exwkvQSPz22W +PIvhkzqeOYRoEDE2bWGC1ukd818qvQp618eLBmJSvwGh4YfUcmgqHaEk2NjoPIMV +-----END CERTIFICATE----- diff --git a/test/configs/certs/client-key.pem b/test/configs/certs/client-key.pem new file mode 100644 index 00000000..bb44aa5a --- /dev/null +++ b/test/configs/certs/client-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/configs/certs/server-cert.pem b/test/configs/certs/server-cert.pem new file mode 100644 index 00000000..46bc9133 --- /dev/null +++ b/test/configs/certs/server-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgIJAO+k4G7bNTyoMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy +MzA2MzRaFw0xOTExMDQyMzA2MzRaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBy3IEY3kqlf5h2vEtk9CB4Vnt +AD+eaVc3Y9xuFHJ4k0ScsjwrYH3YcwQW0fDpOQCI0102YoQT7tPCBT+rC0w1mM82 +0ZSKS/y2SIK9cM6LHWkUbQcWfeaL+uz2HB3jTEm8tmEEFTLBJFMbMpUsvjA5GLqG +URswsNjYEl8M9wS1BETw2e+eCFa4wxq9oGHp/Dgh0vZglHzQL5osEpRct1aaQo6O +jWzZc1Cgx4SxmMOoMWF8BQzlO7aikbZEJxk03TIFNph/azJt7mviMseW72mP+bX9 +sm/8bsINiYgJMM2HAyjIgFVMXX8AYfEFC4wozYloLqn0yy9TdjhyGbsUjg0yTd4l +A9LkGKroBdY1drPSek5Nj9br27UGGGfU2ddAD5xYBIeeFY+3nqST868oIXB/m1P7 +1p8tpkgujx/RqKr3nvOqBHizmoQaWZsPC3X/Jc4NvVHihpuNzN/u1D5mxGhxsx+R +qnrIkhS0IqNrokggPZazugmHntd95HgTb3JpjY3RGEYXAQNr+mZGUCc+CVu0mhFX +xAMZcfVp5nDg4hKHiaRv0KcaqBmnn8AB5w5FiTppzUbRP0zz7GkwrdulwR6c2Eb5 +75+/022TbgCx8B9SH4zJRTj5mtrK56eFgTcnuXB+YnWaP7/7qmKIZzxrd3UDvnza +bhnMiiIK7vL8qiOTAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA +ATANBgkqhkiG9w0BAQsFAAOCAgEAOrh8XfW6quwBAcCxHf6/uvu/iNq4yHCg2qH6 +VtWs/x38I2t3BRSNsLsJGieh6yLlZDzOus+XYui4uDE50XmcwaIsY0VcXnvdyZVZ +w9+lMyfp00kRF1o3B6eVxq0pRE5VB0cai7XI7tyfpRwGzA+oNLF4vBvxAHm9Ony5 +Q57DC/HFzyUogdkMYciO/kd9oa4HosDEXwaE8UvZUL8OVl/dptMXLL/GGwzZsUAE +1sLAbgm044YChLUDzgBAtDTkB/HNkcPzSKwULuskhe7ndoaEQNXVZuP7quGiZ/W1 +1lE59gnmnyG8ySFCL05jHrKLtFAJe88gQjgDK65ZJv4W/k7ocmT+HhCxWyQWcX6v +abJ0EssqeSQuzRMuZebMJJ8s46d6RcYuMdIX3RDXq+1moJDFopE7lgNrlRhWgaky +Og8f/u8s1j75tk1YaYcY9uBKjKk7f681R9wMumkd6IEmEvkUwHNFsctxi4fGI7h1 +PRdKL0DlhVmnpHlKs6Kvm2sJ3twSAGSrC4u0LuxACeR3XbiBfyhFV/291LSuw/y1 +JtWOW5koh0g1k9xtkiu3/ePVdG/CLp796IyRhdB1jP/vD7W5RLLG/VAlomfjsPsB +AnwFYbVZ8KrmMKYUpTJOH31CRzFdOB6nWqXu5tk3nOtLKo1nIOuVtmp9XLz3VtHe +NiZPnqA= +-----END CERTIFICATE----- diff --git a/test/configs/certs/server-key.pem b/test/configs/certs/server-key.pem new file mode 100644 index 00000000..113a87e1 --- /dev/null +++ b/test/configs/certs/server-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/configs/certs/srva-cert.pem b/test/configs/certs/srva-cert.pem new file mode 100644 index 00000000..5204be52 --- /dev/null +++ b/test/configs/certs/srva-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIJAO+k4G7bNTyuMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDgy +MjA4MzBaFw0xOTExMDcyMjA4MzBaMBcxFTATBgNVBAMTDG5hdHMtY2x1c3RlcjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM3qpumhhgGCKwQhy02XOueK +cDs6s79TyGt1Q9mFmO3ZZgowO0lo+qOlHgBfHBrMtZU4tQ4ImrYzLSw1YDd/6DAX +iyUmbzymRYCShF8gzr4v8OGt/M8zuha9L7TAGT+hE+remG6WVT1eYdo3VpwRCxhr +9ysgO23wkU9VggTBzSEhsxzkosppkKMe8llOwOXuZeweh17VsCDDGJqarZd3PRan +RbshQ7Dk4QTmXr8kpinVvwI7TpiEtaGPi8eeMYuJ3MBrSS5465p5ELYZo8GUD/lT +U/li9eUbSduHDlHjzmnRjcwxnJW8jksJs0OJimAjg0kjyd3Bwla5xtT9c3ooDyg0 +LRVV7KWAcVcLqLNvjNDJ3ROHDwzpg7wgwCMkZvp6KRiljonsHg36GMVhfh6JPxpD +5LmREK/dNBEzU6iYAEsBl4LihbREAUwdpkDNFOmox70VURHlMf0q3gBqBlooE9Ob +JadKjms+2yBEDuJQhO9hbbYJMifgdsE6DPQq57uLSZm8rKZHhIbQitVj/3Cw/U/7 +uYF2Z0biZz24nnOUATxeksnli5mbAZJRfcbVvILlcUorBwEerKGRnGrhE1rmaxJ2 +DsPbG1gtv9nyabYjSi2r8Qt3ghu+7KQujx6Wq8J4ext8QCjxz8VuQefv67kGsnTk ++/Yv8NI3AOb0tqxEk5wlAgMBAAGjPTA7MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcE +fwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEL +BQADggIBAITQlXY4Sf3HvU9wnuKhrlTCqBBLkIQd5Vp9JKGZdtKLuK25KG0FkPEx +CWNQyBsKU6CXnP8L+n+7fcGN+Oz1hPtaczS+NExqWBpDELg5Fqi6TWgnhGBt34op +kI5/HjmyfrlA9Uy+uRh+ydoESi7B/svoaTroITbPN+WF3u7/unJkdqV9cTp4ndTr +iJCJXuXTQIqVAACfXmSpBi8oSJuE/MVCUdr7DPBB8jPDox1kpOZdEllyzp+4Bx3u +nGYxsRNyyIAH4fL9yyU9xJxN0fmNm8Xtc5EV89F4NM/qcVUQwcfNT/SyKnIIfovm +rs3It3mL+Pb8e+3SnDDfyXTOVIN94jMKaBXATB3vY/Ek1T+DkUZI0x/7llme580J +tTGK9O3yuRjyJsiG3echCwS5PkdPRf9+iqn/nHBF+f/GivB8MlQAxRNWajsKoOXF +nmLFcc0NpdPPKa4tH7dnLV9SbPDljuJn88W5I62DkJQwx/MnIL/xxn3CGVU/q7qt +k9DQVxAQaPoEPuQHLyHMkPibTV6tGCghAz+l4zerAbz1SklJ8nzqrkW1pf2hfZC6 +jJZAGs0vXIJ4tWCnHNnPZzqaIopXeB97wusWAByHkhDGtQAGBdOmWTm8Ev1QLTDP +8ZGSPsotfQArQc/Usd6pWo8m40cG0GZyP24zvXRf1/x2003owN2e +-----END CERTIFICATE----- diff --git a/test/configs/certs/srva-key.pem b/test/configs/certs/srva-key.pem new file mode 100644 index 00000000..c530256d --- /dev/null +++ b/test/configs/certs/srva-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/configs/certs/srvb-cert.pem b/test/configs/certs/srvb-cert.pem new file mode 100644 index 00000000..9976bc05 --- /dev/null +++ b/test/configs/certs/srvb-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIJAO+k4G7bNTyvMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDgy +MjA4MzdaFw0xOTExMDcyMjA4MzdaMBcxFTATBgNVBAMTDG5hdHMtY2x1c3RlcjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMtmkDRmDs4DssN1lxzUNa8r +hXoddfOnaiUpZD54vu0gJqJ4qu9US5TPsAfVPsnKUvDCtZFrxKGpBbmFwnhuOJaM +UcQuq1n1VqhbVXI+v9Dm6qqj439kn79hq77F7jZHSM8Sdem3Qu7qZR5zosGgKgf3 +IvFEclk+WddSKB8udtTmrWlBgAUxdLmLZ5C5iSO2DLtWTo1rfzlDZP6ZYkeNgyUt +cHDolS6Mhi7NHiZedRovB13xHs6YA5zDx6i7s7wfypBmCIywLwU5dlewW2Bpueq5 +pbmqFk4TT8f0Ui6CEH+lqlMRpDuUtJmZjwx2kmxzsOpU/kTp76HF7gpiLpWZWfme +rjA9EiKJhrJYfp6tj2IrZ+4tsKBn4uWGG4Osx4quEfRR3fVBmOKlx9Lw6WPa9rvu +GzF61obs3D2gCeOK0qjocdvOWZ5jGBh7HVHOF4c+9H7CrVIMpgkkBgdz1KnkW6Oo +JXK8ZczHhwbLb+lAphe60vY0prXe2x6MSl0M+uVXtaewIlVjJZ9fVO7CGpZvviWl +9qzOlMUGmMayekpyYNv7zBvGJ/tlx76XG1N8KeGEq++lIPcNDVOXIp6ny93g6nRO +JNbBMOPaU/mfdo9Flz+1PibPinTKY5iT+w7c57ox9iOxtTrVI9SQrTlf0cRQGhzo +LddoXVD2i8mGCp/Kc0z1AgMBAAGjPTA7MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcE +fwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEL +BQADggIBAB7avQgjetyggPL/DvyrdyygLnWm3Hg90vg5fecbV6W6TXDd122xyZdk +hZIXNts17u5yKreXYdzaeg4lKGTM/NFLVwsnmEjHAmqmwTSVfmh711NJGaKe8Lz7 +2Io5R/HzPQC2cvJ41lMxLowdAkFfdYQUFlzB2IJzq+QWzsFvTypYXb19T8JgpQvh +NYZjUkYmV1UxuHLiIeuLSm6osBADeVI5bjtD61oFAoS3W/UOYDWK+CgDkyFunDhb +fmwO6ibFmOzx3F+zS3mnlJPuFzlCtB9LrOHhoXnVR5o1e/eQD5LNPvydd0RznVbH +duQ8YGI8JC4/hOxg85X5MCWkMSZ41S4sT8rGpp0gF+jOCFwMwCHDe/zkm9y1F51j +wPfKfwxlD44V4KuPRl7Kf2NtTVjMf4iJ1Hm2OYqgFvjD5TybM7vMeuR4e1swOXMn +7GNjiJTNcEMUEIaB5zYjw/NI7DAvOsFQuxH2+X61N2AUe9YhC/PVG35lsuVFPOFy +zYBsonVb0CoSgrhc+NXGrIAcgEDZcgK8wii8/eVggOCfRl+gwbTJB10cS0AXVuj1 +RUwsIwK4xqynU/imLp/DG5TuEED9pHuxUSTwaZ8JG9ybYmZGyjdMNno4WJtNjnka +1zK861lEtsunus1Dm4zREdGBSZDKaRYSmjkfH+2kj8XnZrhpGn1v +-----END CERTIFICATE----- diff --git a/test/configs/certs/srvb-key.pem b/test/configs/certs/srvb-key.pem new file mode 100644 index 00000000..1f6355ad --- /dev/null +++ b/test/configs/certs/srvb-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/configs/srv_a_tls.conf b/test/configs/srv_a_tls.conf new file mode 100644 index 00000000..f56aa956 --- /dev/null +++ b/test/configs/srv_a_tls.conf @@ -0,0 +1,29 @@ +# Copyright 2012-2015 Apcera Inc. All rights reserved. + +# Cluster Server A + +port: 4222 + +cluster { + host: '127.0.0.1' + port: 4244 + + tls { + # Route cert + cert_file: "./configs/certs/srva-cert.pem" + # Private key + key_file: "./configs/certs/srva-key.pem" + + # Optional certificate authority verifying connected routes + # Required when we have self-signed CA, etc. + ca_file: "./configs/certs/ca.pem" + } + + # Routes are actively solicited and connected to from this server. + # Other servers can connect to us if they supply the correct credentials + # in their routes definitions from above. + + routes = [ + nats-route://127.0.0.1:4246 + ] +} diff --git a/test/configs/srv_b_tls.conf b/test/configs/srv_b_tls.conf new file mode 100644 index 00000000..6dc268fc --- /dev/null +++ b/test/configs/srv_b_tls.conf @@ -0,0 +1,29 @@ +# Copyright 2012-2015 Apcera Inc. All rights reserved. + +# Cluster Server B + +port: 4224 + +cluster { + host: '127.0.0.1' + port: 4246 + + tls { + # Route cert + cert_file: "./configs/certs/srvb-cert.pem" + # Private key + key_file: "./configs/certs/srvb-key.pem" + + # Optional certificate authority verifying connected routes + # Required when we have self-signed CA, etc. + ca_file: "./configs/certs/ca.pem" + } + + # Routes are actively solicited and connected to from this server. + # Other servers can connect to us if they supply the correct credentials + # in their routes definitions from above. + + routes = [ + nats-route://127.0.0.1:4244 + ] +} diff --git a/test/configs/tls.conf b/test/configs/tls.conf new file mode 100644 index 00000000..ac6a9aa2 --- /dev/null +++ b/test/configs/tls.conf @@ -0,0 +1,18 @@ + +# Simple TLS config file + +port: 4443 +net: localhost + +tls { + # Server cert + cert_file: "./configs/certs/server-cert.pem" + # Server private key + key_file: "./configs/certs/server-key.pem" +} + +authorization { + user: derek + password: boo + timeout: 1 +} diff --git a/test/configs/tlsverify.conf b/test/configs/tlsverify.conf new file mode 100644 index 00000000..531b477e --- /dev/null +++ b/test/configs/tlsverify.conf @@ -0,0 +1,16 @@ + +# Simple TLS config file + +port: 4443 +net: localhost + +tls { + # Server cert + cert_file: "./configs/certs/server-cert.pem" + # Server private key + key_file: "./configs/certs/server-key.pem" + # Optional certificate authority for clients + ca_file: "./configs/certs/ca.pem" + # Require a client certificate + verify: true +} diff --git a/test/maxpayload_test.go b/test/maxpayload_test.go index 29b027f6..829d82e3 100644 --- a/test/maxpayload_test.go +++ b/test/maxpayload_test.go @@ -34,12 +34,11 @@ func TestMaxPayload(t *testing.T) { if err != nil { t.Fatalf("Could not make a raw connection to the server: %v", err) } - info := make([]byte, 200) + info := make([]byte, 512) _, err = conn.Read(info) if err != nil { t.Fatalf("Expected an info message to be sent by the server: %s", err) } - pub := fmt.Sprintf("PUB bar %d\r\n", size) conn.Write([]byte(pub)) if err != nil { diff --git a/test/opts_test.go b/test/opts_test.go index b94ac7c3..4c8a4d1b 100644 --- a/test/opts_test.go +++ b/test/opts_test.go @@ -19,3 +19,16 @@ func TestServerConfig(t *testing.T) { opts.MaxPayload, sinfo.MaxPayload) } } + +func TestTLSConfig(t *testing.T) { + srv, opts := RunServerWithConfig("./configs/tls.conf") + defer srv.Shutdown() + + c := createClientConn(t, opts.Host, opts.Port) + defer c.Close() + + sinfo := checkInfoMsg(t, c) + if sinfo.TLSRequired != true { + t.Fatal("Expected TLSRequired to be true when configured") + } +} diff --git a/test/test.go b/test/test.go index 59a6857c..619e6b12 100644 --- a/test/test.go +++ b/test/test.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/nats-io/gnatsd/auth" "github.com/nats-io/gnatsd/server" ) @@ -53,7 +54,16 @@ func RunServerWithConfig(configFile string) (srv *server.Server, opts *server.Op panic(fmt.Sprintf("Error processing configuration file: %v", err)) } opts.NoSigs, opts.NoLog = true, true - srv = RunServer(opts) + + // Check for auth + var a server.Auth + if opts.Authorization != "" { + a = &auth.Token{Token: opts.Authorization} + } + if opts.Username != "" { + a = &auth.Plain{Username: opts.Username, Password: opts.Password} + } + srv = RunServerWithAuth(opts, a) return } diff --git a/test/tls_test.go b/test/tls_test.go new file mode 100644 index 00000000..415e193a --- /dev/null +++ b/test/tls_test.go @@ -0,0 +1,131 @@ +// Copyright 2015 Apcera Inc. All rights reserved. + +package test + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "testing" + + "github.com/nats-io/nats" +) + +func TestTLSConnection(t *testing.T) { + srv, opts := RunServerWithConfig("./configs/tls.conf") + defer srv.Shutdown() + + endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) + nurl := fmt.Sprintf("nats://%s:%s@%s/", opts.Username, opts.Password, endpoint) + nc, err := nats.Connect(nurl) + if err == nil { + t.Fatalf("Expected error trying to connect to secure server") + } + + // Do simple SecureConnect + nc, err = nats.SecureConnect(fmt.Sprintf("nats://%s/", endpoint)) + if err == nil { + t.Fatalf("Expected error trying to connect to secure server with no auth") + } + + nc, err = nats.SecureConnect(nurl) + if err != nil { + t.Fatalf("Got an error on SecureConnect: %+v\n", err) + } + subj := "foo-tls" + sub, _ := nc.SubscribeSync(subj) + + nc.Publish(subj, []byte("We are Secure!")) + nc.Flush() + nmsgs, _ := sub.QueuedMsgs() + if nmsgs != 1 { + t.Fatalf("Expected to receive a message over the TLS connection") + } + defer nc.Close() + + // Now do more advanced checking, verifying servername and using rootCA. + // Setup our own TLSConfig using RootCA from our self signed cert. + rootPEM, err := ioutil.ReadFile("./configs/certs/ca.pem") + if err != nil || rootPEM == nil { + t.Fatalf("failed to read root certificate") + } + pool := x509.NewCertPool() + ok := pool.AppendCertsFromPEM([]byte(rootPEM)) + if !ok { + t.Fatalf("failed to parse root certificate") + } + + config := &tls.Config{ + ServerName: opts.Host, + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + copts := nats.DefaultOptions + copts.Url = nurl + copts.Secure = true + copts.TLSConfig = config + + nc, err = copts.Connect() + if err != nil { + t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) + } + nc.Flush() + defer nc.Close() +} + +func TestTLSClientCertificate(t *testing.T) { + srv, opts := RunServerWithConfig("./configs/tlsverify.conf") + defer srv.Shutdown() + + nurl := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) + + _, err := nats.Connect(nurl) + if err == nil { + t.Fatalf("Expected error trying to connect to secure server without a certificate") + } + + _, err = nats.SecureConnect(nurl) + if err == nil { + t.Fatalf("Expected error trying to secure connect to secure server without a certificate") + } + + // Load client certificate to sucessfully connect. + certFile := "./configs/certs/client-cert.pem" + keyFile := "./configs/certs/client-key.pem" + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + t.Fatalf("error parsing X509 certificate/key pair: %v", err) + } + + // Load in root CA for server verification + rootPEM, err := ioutil.ReadFile("./configs/certs/ca.pem") + if err != nil || rootPEM == nil { + t.Fatalf("failed to read root certificate") + } + pool := x509.NewCertPool() + ok := pool.AppendCertsFromPEM([]byte(rootPEM)) + if !ok { + t.Fatalf("failed to parse root certificate") + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + ServerName: opts.Host, + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + copts := nats.DefaultOptions + copts.Url = nurl + copts.Secure = true + copts.TLSConfig = config + + nc, err := copts.Connect() + if err != nil { + t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) + } + nc.Flush() + defer nc.Close() +}