mirror of https://github.com/tidwall/tile38.git
335 lines
8.9 KiB
Go
335 lines
8.9 KiB
Go
// Copyright 2015-2018 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package test
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/gnatsd/server"
|
|
"github.com/nats-io/go-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("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
|
|
nc, err := nats.Connect(nurl)
|
|
if err == nil {
|
|
nc.Close()
|
|
t.Fatalf("Expected error trying to connect to secure server")
|
|
}
|
|
|
|
// Do simple SecureConnect
|
|
nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint))
|
|
if err == nil {
|
|
nc.Close()
|
|
t.Fatalf("Expected error trying to connect to secure server with no auth")
|
|
}
|
|
|
|
// Now do more advanced checking, verifying servername and using rootCA.
|
|
|
|
nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
|
|
if err != nil {
|
|
t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
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")
|
|
}
|
|
}
|
|
|
|
func TestTLSClientCertificate(t *testing.T) {
|
|
srv, opts := RunServerWithConfig("./configs/tlsverify.conf")
|
|
defer srv.Shutdown()
|
|
|
|
nurl := fmt.Sprintf("tls://%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")
|
|
}
|
|
|
|
// Load client certificate to successfully 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.GetDefaultOptions()
|
|
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 TestTLSVerifyClientCertificate(t *testing.T) {
|
|
srv, opts := RunServerWithConfig("./configs/tlsverify_noca.conf")
|
|
defer srv.Shutdown()
|
|
|
|
nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
|
|
|
|
// The client is configured properly, but the server has no CA
|
|
// to verify the client certificate. Connection should fail.
|
|
nc, err := nats.Connect(nurl,
|
|
nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem"),
|
|
nats.RootCAs("./configs/certs/ca.pem"))
|
|
if err == nil {
|
|
nc.Close()
|
|
t.Fatal("Expected failure to connect, did not")
|
|
}
|
|
}
|
|
|
|
func TestTLSConnectionTimeout(t *testing.T) {
|
|
opts := LoadConfig("./configs/tls.conf")
|
|
opts.TLSTimeout = 0.25
|
|
|
|
srv := RunServer(opts)
|
|
defer srv.Shutdown()
|
|
|
|
// Dial with normal TCP
|
|
endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
|
|
conn, err := net.Dial("tcp", endpoint)
|
|
if err != nil {
|
|
t.Fatalf("Could not connect to %q", endpoint)
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Read deadlines
|
|
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
|
|
|
// Read the INFO string.
|
|
br := bufio.NewReader(conn)
|
|
info, err := br.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatalf("Failed to read INFO - %v", err)
|
|
}
|
|
if !strings.HasPrefix(info, "INFO ") {
|
|
t.Fatalf("INFO response incorrect: %s\n", info)
|
|
}
|
|
wait := time.Duration(opts.TLSTimeout * float64(time.Second))
|
|
time.Sleep(wait)
|
|
// Read deadlines
|
|
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
|
tlsErr, err := br.ReadString('\n')
|
|
if err == nil && !strings.Contains(tlsErr, "-ERR 'Secure Connection - TLS Required") {
|
|
t.Fatalf("TLS Timeout response incorrect: %q\n", tlsErr)
|
|
}
|
|
}
|
|
|
|
// Ensure there is no race between authorization timeout and TLS handshake.
|
|
func TestTLSAuthorizationShortTimeout(t *testing.T) {
|
|
opts := LoadConfig("./configs/tls.conf")
|
|
opts.AuthTimeout = 0.001
|
|
|
|
srv := RunServer(opts)
|
|
defer srv.Shutdown()
|
|
|
|
endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
|
|
nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
|
|
|
|
// Expect an error here (no CA) but not a TLS oversized record error which
|
|
// indicates the authorization timeout fired too soon.
|
|
_, err := nats.Connect(nurl)
|
|
if err == nil {
|
|
t.Fatal("Expected error trying to connect to secure server")
|
|
}
|
|
if strings.Contains(err.Error(), "oversized record") {
|
|
t.Fatal("Corrupted TLS handshake:", err)
|
|
}
|
|
}
|
|
|
|
func stressConnect(t *testing.T, wg *sync.WaitGroup, errCh chan error, url string, index int) {
|
|
defer wg.Done()
|
|
|
|
subName := fmt.Sprintf("foo.%d", index)
|
|
|
|
for i := 0; i < 33; i++ {
|
|
nc, err := nats.Connect(url, nats.RootCAs("./configs/certs/ca.pem"))
|
|
if err != nil {
|
|
errCh <- fmt.Errorf("Unable to create TLS connection: %v\n", err)
|
|
return
|
|
}
|
|
defer nc.Close()
|
|
|
|
sub, err := nc.SubscribeSync(subName)
|
|
if err != nil {
|
|
errCh <- fmt.Errorf("Unable to subscribe on '%s': %v\n", subName, err)
|
|
return
|
|
}
|
|
|
|
if err := nc.Publish(subName, []byte("secure data")); err != nil {
|
|
errCh <- fmt.Errorf("Unable to send on '%s': %v\n", subName, err)
|
|
}
|
|
|
|
if _, err := sub.NextMsg(2 * time.Second); err != nil {
|
|
errCh <- fmt.Errorf("Unable to get next message: %v\n", err)
|
|
}
|
|
|
|
nc.Close()
|
|
}
|
|
|
|
errCh <- nil
|
|
}
|
|
|
|
func TestTLSStressConnect(t *testing.T) {
|
|
opts, err := server.ProcessConfigFile("./configs/tls.conf")
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Error processing configuration file: %v", err))
|
|
}
|
|
opts.NoSigs, opts.NoLog = true, true
|
|
|
|
// For this test, remove the authorization
|
|
opts.Username = ""
|
|
opts.Password = ""
|
|
|
|
// Increase ssl timeout
|
|
opts.TLSTimeout = 2.0
|
|
|
|
srv := RunServer(opts)
|
|
defer srv.Shutdown()
|
|
|
|
nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)
|
|
|
|
threadCount := 3
|
|
|
|
errCh := make(chan error, threadCount)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(threadCount)
|
|
|
|
for i := 0; i < threadCount; i++ {
|
|
go stressConnect(t, &wg, errCh, nurl, i)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
var lastError error
|
|
for i := 0; i < threadCount; i++ {
|
|
err := <-errCh
|
|
if err != nil {
|
|
lastError = err
|
|
}
|
|
}
|
|
|
|
if lastError != nil {
|
|
t.Fatalf("%v\n", lastError)
|
|
}
|
|
}
|
|
|
|
func TestTLSBadAuthError(t *testing.T) {
|
|
srv, opts := RunServerWithConfig("./configs/tls.conf")
|
|
defer srv.Shutdown()
|
|
|
|
endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
|
|
nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, "NOT_THE_PASSWORD", endpoint)
|
|
|
|
_, err := nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
|
|
if err == nil {
|
|
t.Fatalf("Expected error trying to connect to secure server")
|
|
}
|
|
if err.Error() != nats.ErrAuthorization.Error() {
|
|
t.Fatalf("Excpected and auth violation, got %v\n", err)
|
|
}
|
|
}
|
|
|
|
func TestTLSConnectionCurvePref(t *testing.T) {
|
|
srv, opts := RunServerWithConfig("./configs/tls_curve_pref.conf")
|
|
defer srv.Shutdown()
|
|
|
|
if len(opts.TLSConfig.CurvePreferences) != 1 {
|
|
t.Fatal("Invalid curve preference loaded.")
|
|
}
|
|
|
|
if opts.TLSConfig.CurvePreferences[0] != tls.CurveP256 {
|
|
t.Fatalf("Invalid curve preference loaded [%v].", opts.TLSConfig.CurvePreferences[0])
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
|
|
nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
|
|
nc, err := nats.Connect(nurl)
|
|
if err == nil {
|
|
nc.Close()
|
|
t.Fatalf("Expected error trying to connect to secure server")
|
|
}
|
|
|
|
// Do simple SecureConnect
|
|
nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint))
|
|
if err == nil {
|
|
nc.Close()
|
|
t.Fatalf("Expected error trying to connect to secure server with no auth")
|
|
}
|
|
|
|
// Now do more advanced checking, verifying servername and using rootCA.
|
|
|
|
nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem"))
|
|
if err != nil {
|
|
t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
|
|
}
|
|
defer nc.Close()
|
|
|
|
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")
|
|
}
|
|
}
|