Skip to content

Commit

Permalink
Add TLS support for NATS Connections (#13)
Browse files Browse the repository at this point in the history
* Add TLS support for NATS connections

Signed-off-by: Colin Sullivan <[email protected]>
  • Loading branch information
ColinSullivan1 authored Nov 15, 2019
1 parent c2c6cf9 commit d718810
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 27 deletions.
9 changes: 6 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ func main() {
flag.IntVar(&opts.ListenPort, "p", surveyor.DefaultListenPort, "Port to listen on.")
flag.StringVar(&opts.ListenAddress, "addr", surveyor.DefaultListenAddress, "Network host to listen on.")
flag.StringVar(&opts.ListenAddress, "a", surveyor.DefaultListenAddress, "Network host to listen on.")
flag.StringVar(&opts.CertFile, "tlscert", "", "Server certificate file (Enables HTTPS).")
flag.StringVar(&opts.KeyFile, "tlskey", "", "Private key for server certificate (used with HTTPS).")
flag.StringVar(&opts.CaFile, "tlscacert", "", "Client certificate CA for verification (used with HTTPS).")
flag.StringVar(&opts.CertFile, "tlscert", "", "Client certificate file for NATS connections.")
flag.StringVar(&opts.KeyFile, "tlskey", "", "Client private key for NATS connections.")
flag.StringVar(&opts.CaFile, "tlscacert", "", "Client certificate CA on NATS connecctions.")
flag.StringVar(&opts.HTTPCertFile, "http_tlscert", "", "Server certificate file (Enables HTTPS).")
flag.StringVar(&opts.HTTPKeyFile, "http_tlskey", "", "Private key for server certificate (used with HTTPS).")
flag.StringVar(&opts.HTTPCaFile, "http_tlscacert", "", "Client certificate CA for verification (used with HTTPS).")
flag.StringVar(&opts.HTTPUser, "http_user", "", "Enable basic auth and set user name for HTTP scrapes.")
flag.StringVar(&opts.HTTPPassword, "http_pass", "", "Set the password for HTTP scrapes. NATS bcrypt supported.")
flag.StringVar(&opts.Prefix, "prefix", "", "Replace the default prefix for all the metrics.")
Expand Down
29 changes: 20 additions & 9 deletions surveyor/surveyor.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ type Options struct {
CertFile string
KeyFile string
CaFile string
HTTPCertFile string
HTTPKeyFile string
HTTPCaFile string
NATSServerURL string
HTTPUser string // User in metrics scrape by Prometheus.
HTTPPassword string
Expand Down Expand Up @@ -120,6 +123,14 @@ func connect(opts *Options) (*nats.Conn, error) {
}))
nopts = append(nopts, nats.MaxReconnects(10240))

// NATS TLS Options
if opts.CaFile != "" {
nopts = append(nopts, nats.RootCAs(opts.CaFile))
}
if opts.CertFile != "" {
nopts = append(nopts, nats.ClientCert(opts.CertFile, opts.KeyFile))
}

nc, err := nats.Connect(opts.URLs, nopts...)
if err != nil {
return nil, err
Expand Down Expand Up @@ -164,17 +175,17 @@ func (s *Surveyor) createCollector() error {
}

// generates the TLS config for https
func (s *Surveyor) generateTLSConfig() (*tls.Config, error) {
func (s *Surveyor) generateHTTPTLSConfig() (*tls.Config, error) {
// Load in cert and private key
cert, err := tls.LoadX509KeyPair(s.opts.CertFile, s.opts.KeyFile)
cert, err := tls.LoadX509KeyPair(s.opts.HTTPCertFile, s.opts.HTTPKeyFile)
if err != nil {
return nil, fmt.Errorf("error parsing X509 certificate/key pair (%s, %s): %v",
s.opts.CertFile, s.opts.KeyFile, err)
s.opts.HTTPCertFile, s.opts.HTTPKeyFile, err)
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("error parsing certificate (%s): %v",
s.opts.CertFile, err)
s.opts.HTTPCertFile, err)
}
// Create our TLS configuration
config := &tls.Config{
Expand All @@ -183,10 +194,10 @@ func (s *Surveyor) generateTLSConfig() (*tls.Config, error) {
MinVersion: tls.VersionTLS12,
}
// Add in CAs if applicable.
if s.opts.CaFile != "" {
rootPEM, err := ioutil.ReadFile(s.opts.CaFile)
if s.opts.HTTPCaFile != "" {
rootPEM, err := ioutil.ReadFile(s.opts.HTTPCaFile)
if err != nil || rootPEM == nil {
return nil, fmt.Errorf("failed to load root ca certificate (%s): %v", s.opts.CaFile, err)
return nil, fmt.Errorf("failed to load root ca certificate (%s): %v", s.opts.HTTPCaFile, err)
}
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(rootPEM)
Expand Down Expand Up @@ -284,11 +295,11 @@ func (s *Surveyor) startHTTP() error {

// If a certificate file has been specified, setup TLS with the
// key provided.
if s.opts.CertFile != "" {
if s.opts.HTTPCertFile != "" {
proto = "https"
// debug
log.Printf("Certificate file specfied; using https.")
config, err = s.generateTLSConfig()
config, err = s.generateHTTPTLSConfig()
if err != nil {
return err
}
Expand Down
44 changes: 41 additions & 3 deletions surveyor/surveyor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,52 @@ func TestSurveyor_NoSystemAccount(t *testing.T) {
}
}

func TestSurveyor_ClientTLSFail(t *testing.T) {
ns := st.StartServer(t, "../test/r1s1.conf")
st.ConnectAndVerify(t, ns.ClientURL())
defer ns.Shutdown()

opts := getTestOptions()
opts.CaFile = caCertFile
opts.CertFile = clientCert
opts.KeyFile = clientKey

_, err := NewSurveyor(opts)
if err == nil {
t.Fatalf("Connected to a server that required TLS")
}
}

func TestSurveyor_ClientTLS(t *testing.T) {
ns := st.StartServer(t, "../test/tls.conf")
defer ns.Shutdown()

opts := getTestOptions()
opts.URLs = "127.0.0.1:4223"
opts.CaFile = caCertFile
opts.CertFile = clientCert
opts.KeyFile = clientKey

s, err := NewSurveyor(opts)
if err != nil {
t.Fatalf("couldn't create surveyor: %v", err)
}
if err = s.Start(); err != nil {
t.Fatalf("start error: %v", err)
}
defer s.Stop()

pollAndCheckDefault(t, "nats_core_mem_bytes")
}

func TestSurveyor_HTTPS(t *testing.T) {
sc := st.NewSuperCluster(t)
defer sc.Shutdown()

opts := getTestOptions()
opts.CaFile = caCertFile
opts.CertFile = serverCert
opts.KeyFile = serverKey
opts.HTTPCaFile = caCertFile
opts.HTTPCertFile = serverCert
opts.HTTPKeyFile = serverKey

s, err := NewSurveyor(opts)
if err != nil {
Expand Down
5 changes: 0 additions & 5 deletions test/certs/oo.sh

This file was deleted.

16 changes: 9 additions & 7 deletions test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ func StartBasicServer() *ns.Server {
panic("Unable to start NATS Server in Go Routine")
}

// startServer starts a a NATS server
func startServer(t *testing.T, confFile string) *ns.Server {
// StartServer starts a a NATS server
func StartServer(t *testing.T, confFile string) *ns.Server {
resetPreviousHTTPConnections()
opts, err := ns.ProcessConfigFile(confFile)

Expand Down Expand Up @@ -138,16 +138,16 @@ var configFiles = []string{"../test/r1s1.conf", "../test/r1s2.conf", "../test/r2
func NewSuperCluster(t *testing.T) *SuperCluster {
sc := &SuperCluster{}
for _, f := range configFiles {
sc.Servers = append(sc.Servers, startServer(t, f))
sc.Servers = append(sc.Servers, StartServer(t, f))
}
sc.setupClientsAndVerify(t)
return sc
}

// NewSingleServer creates a single NATS server with a system account
func NewSingleServer(t *testing.T) *ns.Server {
s := startServer(t, "../test/r1s1.conf")
connectAndVerify(t, s.ClientURL())
s := StartServer(t, "../test/r1s1.conf")
ConnectAndVerify(t, s.ClientURL())
return s
}

Expand All @@ -161,7 +161,9 @@ func (sc *SuperCluster) Shutdown() {
}
}

func connectAndVerify(t *testing.T, url string) *nats.Conn {
// ConnectAndVerify connects to a server a verifies it is
// ready to process messages.
func ConnectAndVerify(t *testing.T, url string) *nats.Conn {
c, err := nats.Connect(url, nats.UserCredentials("../test/myuser.creds"))
if err != nil {
t.Fatalf("Couldn't connect a client to %s: %v", url, err)
Expand All @@ -185,7 +187,7 @@ func connectAndVerify(t *testing.T, url string) *nats.Conn {

func (sc *SuperCluster) setupClientsAndVerify(t *testing.T) {
for _, s := range sc.Servers {
c := connectAndVerify(t, s.ClientURL())
c := ConnectAndVerify(t, s.ClientURL())
sc.Clients = append(sc.Clients, c)
}

Expand Down
6 changes: 6 additions & 0 deletions test/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ func TestStartSingleServer(t *testing.T) {
ns := NewSingleServer(t)
ns.Shutdown()
}

func TestStartServers(t *testing.T) {
ns := StartServer(t, "../test/r1s1.conf")
ConnectAndVerify(t, ns.ClientURL())
ns.Shutdown()
}
13 changes: 13 additions & 0 deletions test/tls.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Simple TLS config file

listen: 127.0.0.1:4223

tls {
# Server cert
cert_file: "../test/certs/server-cert.pem"
# Server private key
key_file: "../test/certs/server-key.pem"
# Specified time for handshake to complete
timeout: 2
}

0 comments on commit d718810

Please sign in to comment.