diff --git a/server/gateway.go b/server/gateway.go index 5b4fbe11..7ef33144 100644 --- a/server/gateway.go +++ b/server/gateway.go @@ -201,6 +201,9 @@ func validateGatewayOptions(o *Options) error { if o.Gateway.Port == 0 { return fmt.Errorf("gateway %q has no port specified (select -1 for random port)", o.Gateway.Name) } + if o.Gateway.TLSConfig != nil && o.Gateway.TLSConfig.InsecureSkipVerify { + return fmt.Errorf("tls InsecureSkipVerify not supported for gateway") + } for i, g := range o.Gateway.Gateways { if g.Name == "" { return fmt.Errorf("gateway in the list %d has no name", i) @@ -208,6 +211,9 @@ func validateGatewayOptions(o *Options) error { if len(g.URLs) == 0 { return fmt.Errorf("gateway %q has no URL", g.Name) } + if g.TLSConfig != nil && g.TLSConfig.InsecureSkipVerify { + return fmt.Errorf("tls InsecureSkipVerify not supported for remote gateway %q", g.Name) + } } return nil } diff --git a/server/opts.go b/server/opts.go index 1556522c..274eae3f 100644 --- a/server/opts.go +++ b/server/opts.go @@ -233,6 +233,7 @@ type TLSConfigOpts struct { KeyFile string CaFile string Verify bool + Insecure bool Map bool Timeout float64 Ciphers []uint16 @@ -1846,6 +1847,12 @@ func parseTLS(v interface{}) (*TLSConfigOpts, error) { return nil, &configErr{tk, fmt.Sprintf("error parsing tls config, expected 'ca_file' to be filename")} } tc.CaFile = caFile + case "insecure": + insecure, ok := mv.(bool) + if !ok { + return nil, &configErr{tk, fmt.Sprintf("error parsing tls config, expected 'insecure' to be a boolean")} + } + tc.Insecure = insecure case "verify": verify, ok := mv.(bool) if !ok { @@ -1936,6 +1943,7 @@ func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) { PreferServerCipherSuites: true, CurvePreferences: tc.CurvePreferences, Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: tc.Insecure, } // Require client certificates as needed diff --git a/server/server.go b/server/server.go index e32c4fb7..366ae4d6 100644 --- a/server/server.go +++ b/server/server.go @@ -292,6 +292,11 @@ func NewServer(opts *Options) (*Server, error) { } func validateOptions(o *Options) error { + // For now, InsecureSkipVerify is supported only for Cluster. + // So fail if it was set to client and or gateways. + if o.TLSConfig != nil && o.TLSConfig.InsecureSkipVerify { + return fmt.Errorf("tls InsecureSkipVerify not supported for client connections") + } // Check that the trust configuration is correct. if err := validateTrustedOperators(o); err != nil { return err diff --git a/server/server_test.go b/server/server_test.go index 1c34eb14..9b6f2fd0 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1085,3 +1085,57 @@ func TestClientWriteLoopStall(t *testing.T) { case <-time.After(250 * time.Millisecond): } } + +func TestInsecureSkipVerifyNotSupportedForClientAndGateways(t *testing.T) { + checkServerFails := func(t *testing.T, o *Options) { + t.Helper() + s, err := NewServer(o) + if s != nil { + s.Shutdown() + } + if err == nil || !strings.Contains(err.Error(), "not supported") { + t.Fatalf("Expected error about not supported feature, got %v", err) + } + } + + tc := &TLSConfigOpts{} + tc.CertFile = "../test/configs/certs/server-cert.pem" + tc.KeyFile = "../test/configs/certs/server-key.pem" + tc.CaFile = "../test/configs/certs/ca.pem" + tc.Insecure = true + + o := DefaultOptions() + config, err := GenTLSConfig(tc) + if err != nil { + t.Fatalf("Error generating tls config: %v", err) + } + o.TLSConfig = config + checkServerFails(t, o) + + // Get a clone that we will use for the Gateway TLS setting + gwConfig := config.Clone() + + // Remove the setting + o.TLSConfig.InsecureSkipVerify = false + // Configure GW + o.Gateway.Name = "A" + o.Gateway.Host = "127.0.0.1" + o.Gateway.Port = -1 + o.Gateway.TLSConfig = gwConfig + checkServerFails(t, o) + + // Get a config for the remote gateway + rgwConfig := gwConfig.Clone() + gurl, _ := url.Parse("nats://127.0.0.1:1234") + + // Remove the insecure for the main gateway config + o.Gateway.TLSConfig.InsecureSkipVerify = false + o.Gateway.Gateways = []*RemoteGatewayOpts{ + &RemoteGatewayOpts{ + Name: "B", + URLs: []*url.URL{gurl}, + TLSConfig: rgwConfig, + }, + } + checkServerFails(t, o) +} diff --git a/test/cluster_tls_test.go b/test/cluster_tls_test.go index e215667f..41367542 100644 --- a/test/cluster_tls_test.go +++ b/test/cluster_tls_test.go @@ -14,7 +14,11 @@ package test import ( + "fmt" + "io/ioutil" + "strings" "testing" + "time" "github.com/nats-io/gnatsd/server" ) @@ -62,3 +66,78 @@ func TestBasicTLSClusterPubSub(t *testing.T) { matches := expectMsgs(1) checkMsg(t, matches[0], "foo", "22", "", "2", "ok") } + +type captureTLSError struct { + dummyLogger + ch chan struct{} +} + +func (c *captureTLSError) Errorf(format string, v ...interface{}) { + msg := fmt.Sprintf(format, v...) + if strings.Contains(msg, "handshake error") { + select { + case c.ch <- struct{}{}: + default: + } + } +} + +func TestClusterTLSInsecure(t *testing.T) { + confA := createConfFile(t, []byte(` + port: -1 + cluster { + listen: "127.0.0.1:-1" + tls { + cert_file: "./configs/certs/server-noip.pem" + key_file: "./configs/certs/server-key-noip.pem" + ca_file: "./configs/certs/ca.pem" + timeout: 2 + } + } + `)) + srvA, optsA := RunServerWithConfig(confA) + defer srvA.Shutdown() + + l := &captureTLSError{ch: make(chan struct{}, 1)} + srvA.SetLogger(l, false, false) + + bConfigTemplate := ` + port: -1 + cluster { + listen: "127.0.0.1:-1" + tls { + cert_file: "./configs/certs/server-noip.pem" + key_file: "./configs/certs/server-key-noip.pem" + ca_file: "./configs/certs/ca.pem" + timeout: 2 + %s + } + routes [ + "nats://%s:%d" + ] + } + ` + confB := createConfFile(t, []byte(fmt.Sprintf(bConfigTemplate, + "", optsA.Cluster.Host, optsA.Cluster.Port))) + srvB, _ := RunServerWithConfig(confB) + defer srvB.Shutdown() + + // We should get errors + select { + case <-l.ch: + case <-time.After(2 * time.Second): + t.Fatalf("Did not get handshake error") + } + + // Need to add "insecure: true" and reload + if err := ioutil.WriteFile(confB, + []byte(fmt.Sprintf(bConfigTemplate, "insecure: true", optsA.Cluster.Host, optsA.Cluster.Port)), + 0666); err != nil { + t.Fatalf("Error rewriting file: %v", err) + } + if err := srvB.Reload(); err != nil { + t.Fatalf("Error on reload: %v", err) + } + + checkClusterFormed(t, srvA, srvB) +} diff --git a/test/configs/certs/server-key-noip.pem b/test/configs/certs/server-key-noip.pem new file mode 100644 index 00000000..1cd7baca --- /dev/null +++ b/test/configs/certs/server-key-noip.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEArFsnvZER/Hy0ZVfBInmgn8A6CFM+TRx3dcaqw5qB7qo6yGQI +fCtGSkF5bxlevf820QHA3rRO8jboSOi4CdQ0Bis9c8XleBQFuy/EMJMcZwWpW5jO +XuMOyKbJZPtucceJdl5jbYJum3whPklFa4iIifQWth9BIPQRqw06BXU7aVgKEfJi +3BFIihamQFj1YUcDAvg5zPn2rXo3m3RRVtMmqWDldxPnRFQcvmoon723J5yn5wcR +JO21sH4vgHm8tKuxFpVH5go0tmrJUPRkG6E6zURY8+qBhwXeQPzH1kUzP2w6z21k +NFYrfxhdviSJyVzqMPkqAKcTRNb3o1Po9o/r4wIDAQABAoIBABmg4FLmzGbf4C8l +klle/oxil/5+6kerEPRZWzEEu4dSlrUPcteL08Lc44KqohTcazk+6n/zDb4kffvB +ZwRHIok8SsH5TjrBt2xzGNgkN9f11o4BNcaUOrGJY275mB2hDwaj2GMDPxEtrv/F +A2aPAcaJngrssNGGmU33BIalPPVX3HHXy/DgyJGJtMOqjba3AclOjL3dNETExIG1 +LEZVu29xzUo1+g4iyM4Rtwred/rvprw7l+k7tC0EqJqu8PEMJanBwEt1Vc66Its3 +02b3l+X8E2tU43arSP2mOMmsgnyCVchIiU/UXbRyto9ngXl2tUyapLvzwPGGfuX6 +GXuA/9kCgYEA3T6OiMioN34jGkBjIH56D1p7iLfoxaNsb2CjHyFrgOcbGgDRJL6Z +fB7JNK3bZDMzOSBO6Fz9GogsXh0bX+6SDb+xTjqdDxv4Icz12e2lFUUKLzaLZtgX ++2yay89geKmj78gqsF+WBy8lB36D5zBXXD57udVRmKCH4oQlAHC9TCUCgYEAx26I +m+4JHgP7/3I51ZaSnpaJMeYf0gWWRfHmgF/bWJl+3CSbYy+e3Jn+SXAfqw2j/zYK +4R8PmAMtp3qM8uFW9aU+IdSuBsl/qzyTKJDxlvNHE8R0zvwyELmOPN0d1dpMhyuO +ohAhAtlvpVFboJoex6Cv5Mq6CvzM9OcLZ9ZGVWcCgYEAgIveSP89LbDuOEx8y95w +kQcji5Y/esWvNS9S172VNroQdxVObykHCKTihucU96z/8AsjjcKplIbWMIB87Uoy +NibQoAFSS/sWOp1Zoxt+tVL9zdzFNiYMGRtK/WwqQzFdfO4yT3PaOaZcv3P0s5jU +yVXMkXauCB4NlcDttsnKUrkCgYBGpbFfC4PPTjoG08AJGb32z/zp4EuFatVBEeBF +ua9KQ5XdBlrFWCk+nI6oFUAiqEJgNhTEiuxLPVT+7zrfD1Ru0IqiEWVUoizHzD2V +MUNj5epX5lA6wrw5kIICrZHUH/DcMWHlpzEKgVw7hAahbfdZYGLu+aHOIp+4YeB4 +eD+SIQKBgBc7SodbrugKLrLCaH3pzl4jahbbVvQ6G6T2RwDTeFG8UGI2yLvyfZ3Y +NTYq/UaNOrKpNL4ThB2FZmfpRiWuJ9FvWXmuMWmHzKIcT142tN+EBye7y8V6mURE +SU49GLqU+6fNvu2KdFZAUQnJIzc+WXrT8Qsr5ka/ENHLp8I3o03e +-----END RSA PRIVATE KEY----- diff --git a/test/configs/certs/server-noip.pem b/test/configs/certs/server-noip.pem new file mode 100644 index 00000000..a7317be2 --- /dev/null +++ b/test/configs/certs/server-noip.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEjzCCAncCCQDvpOBu2zU8sDANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQK +EwpBcGNlcmEgSW5jMRAwDgYDVQQLEwduYXRzLmlvMRIwEAYDVQQDEwlsb2NhbGhv +c3QxHDAaBgkqhkiG9w0BCQEWDWRlcmVrQG5hdHMuaW8wHhcNMTgxMTI0MjA1MTQ1 +WhcNMjgxMTIxMjA1MTQ1WjCBhjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQw +EgYDVQQHDAtMb3MgQW5nZWxlczEQMA4GA1UECgwHU3luYWRpYTEQMA4GA1UECwwH +TkFUUy5pbzESMBAGA1UEAwwJbG9jYWxob3N0MRwwGgYJKoZIhvcNAQkBFg1kZXJl +a0BuYXRzLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArFsnvZER +/Hy0ZVfBInmgn8A6CFM+TRx3dcaqw5qB7qo6yGQIfCtGSkF5bxlevf820QHA3rRO +8jboSOi4CdQ0Bis9c8XleBQFuy/EMJMcZwWpW5jOXuMOyKbJZPtucceJdl5jbYJu +m3whPklFa4iIifQWth9BIPQRqw06BXU7aVgKEfJi3BFIihamQFj1YUcDAvg5zPn2 +rXo3m3RRVtMmqWDldxPnRFQcvmoon723J5yn5wcRJO21sH4vgHm8tKuxFpVH5go0 +tmrJUPRkG6E6zURY8+qBhwXeQPzH1kUzP2w6z21kNFYrfxhdviSJyVzqMPkqAKcT +RNb3o1Po9o/r4wIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBaw+X7f5kzrvhDFAc9 +ToSRfKW4P7gcnSTODMq3HqgI3mpjmgJKx7WJMYb7YTk4xrbREPQzr29rRCdCncOs +sM8hZZ/4CgVg+l7CgZFFviJ6tv0EWgmKj2H7V0nsC9qsEoCVNWQngTvs8vahLY9R +LBV1cGN33t2FA/y8Y2+ioSKxf2iyq7uGx9uVT/upaOtLm12Qex8Q1eb9xAE5Zs4m +wbZqIDocR5FCzWXSKOkpXmk1ii9oMars++dGq6XWoFhU2Yr3Yh443aLJCSVAcayW +QHlWAl73SKAorhvDl+7EWAK6dzSVMv52xCsyAzDCx1ek119e09NzxAjth9mcI7PC +a9sH2WJuUuQUu9lBAa13QvIepZLBDw6AaFnsh4ve7xFPPiRr9JRLUxRUxiUvLMDO +8UDLRcYxqbMMSECbkmiZeylROTCXj3WLXtqWMoeDmiRX5m46E9FEAkPCkz1XFeeW +5tuRhk4gdyvWWWYNIQBWwcjnK0M0BxFfFOMNc+BDiq8wydFBk79b8oyk7ayYSuzk +MEC5R/YwL+pEYdK1QqaGoNySXPnserNOw9KFxXsEINTJGSl0WJqW0qPEAEfgEBRY +E2s//VCHsqY9/yTo3DMNy00DkFTtGqOCIA21NN/xPtPcxwSAvizgRbeNMysXiW/q +b7bzpfVnBWoHkt/QMi+jI1A3iQ== +-----END CERTIFICATE-----