// Copyright 2019 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 server import ( "context" "fmt" "net/url" "os" "strings" "testing" "time" ) type captureLeafNodeRandomIPLogger struct { DummyLogger ch chan struct{} ips [3]int } func (c *captureLeafNodeRandomIPLogger) Debugf(format string, v ...interface{}) { msg := fmt.Sprintf(format, v...) if strings.Contains(msg, "hostname_to_resolve") { ippos := strings.Index(msg, "127.0.0.") if ippos != -1 { n := int(msg[ippos+8] - '1') c.ips[n]++ for _, v := range c.ips { if v < 2 { return } } // All IPs got at least some hit, we are done. c.ch <- struct{}{} } } } func TestLeafNodeRandomIP(t *testing.T) { u, err := url.Parse("nats://hostname_to_resolve:1234") if err != nil { t.Fatalf("Error parsing: %v", err) } resolver := &myDummyDNSResolver{ips: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}} o := DefaultOptions() o.Host = "127.0.0.1" o.Port = -1 o.LeafNode.Port = 0 o.LeafNode.Remotes = []*RemoteLeafOpts{{URL: u}} o.LeafNode.ReconnectInterval = 50 * time.Millisecond o.LeafNode.resolver = resolver o.LeafNode.dialTimeout = 15 * time.Millisecond s := RunServer(o) defer s.Shutdown() l := &captureLeafNodeRandomIPLogger{ch: make(chan struct{})} s.SetLogger(l, true, true) select { case <-l.ch: case <-time.After(3 * time.Second): t.Fatalf("Does not seem to have used random IPs") } } type testLoopbackResolver struct{} func (r *testLoopbackResolver) LookupHost(ctx context.Context, host string) ([]string, error) { return []string{"127.0.0.1"}, nil } func TestLeafNodeTLSWithCerts(t *testing.T) { conf1 := createConfFile(t, []byte(` port: -1 leaf { listen: "127.0.0.1:-1" tls { ca_file: "../test/configs/certs/tlsauth/ca.pem" cert_file: "../test/configs/certs/tlsauth/server.pem" key_file: "../test/configs/certs/tlsauth/server-key.pem" timeout: 2 } } `)) defer os.Remove(conf1) s1, o1 := RunServerWithConfig(conf1) defer s1.Shutdown() u, err := url.Parse(fmt.Sprintf("nats://localhost:%d", o1.LeafNode.Port)) if err != nil { t.Fatalf("Error parsing url: %v", err) } conf2 := createConfFile(t, []byte(fmt.Sprintf(` port: -1 leaf { remotes [ { url: "%s" tls { ca_file: "../test/configs/certs/tlsauth/ca.pem" cert_file: "../test/configs/certs/tlsauth/client.pem" key_file: "../test/configs/certs/tlsauth/client-key.pem" timeout: 2 } } ] } `, u.String()))) defer os.Remove(conf2) o2, err := ProcessConfigFile(conf2) if err != nil { t.Fatalf("Error processing config file: %v", err) } o2.NoLog, o2.NoSigs = true, true o2.LeafNode.resolver = &testLoopbackResolver{} s2 := RunServer(o2) defer s2.Shutdown() checkFor(t, 3*time.Second, 10*time.Millisecond, func() error { if nln := s1.NumLeafNodes(); nln != 1 { return fmt.Errorf("Number of leaf nodes is %d", nln) } return nil }) } func TestLeafNodeTLSRemoteWithNoCerts(t *testing.T) { conf1 := createConfFile(t, []byte(` port: -1 leaf { listen: "127.0.0.1:-1" tls { ca_file: "../test/configs/certs/tlsauth/ca.pem" cert_file: "../test/configs/certs/tlsauth/server.pem" key_file: "../test/configs/certs/tlsauth/server-key.pem" timeout: 2 } } `)) defer os.Remove(conf1) s1, o1 := RunServerWithConfig(conf1) defer s1.Shutdown() u, err := url.Parse(fmt.Sprintf("nats://localhost:%d", o1.LeafNode.Port)) if err != nil { t.Fatalf("Error parsing url: %v", err) } conf2 := createConfFile(t, []byte(fmt.Sprintf(` port: -1 leaf { remotes [ { url: "%s" tls { ca_file: "../test/configs/certs/tlsauth/ca.pem" timeout: 5 } } ] } `, u.String()))) defer os.Remove(conf2) o2, err := ProcessConfigFile(conf2) if err != nil { t.Fatalf("Error processing config file: %v", err) } if len(o2.LeafNode.Remotes) == 0 { t.Fatal("Expected at least a single leaf remote") } var ( got float64 = o2.LeafNode.Remotes[0].TLSTimeout expected float64 = 5 ) if got != expected { t.Fatalf("Expected %v, got: %v", expected, got) } o2.NoLog, o2.NoSigs = true, true o2.LeafNode.resolver = &testLoopbackResolver{} s2 := RunServer(o2) defer s2.Shutdown() checkFor(t, 3*time.Second, 10*time.Millisecond, func() error { if nln := s1.NumLeafNodes(); nln != 1 { return fmt.Errorf("Number of leaf nodes is %d", nln) } return nil }) // Here we only process options without starting the server // and without a root CA for the remote. conf3 := createConfFile(t, []byte(fmt.Sprintf(` port: -1 leaf { remotes [ { url: "%s" tls { timeout: 10 } } ] } `, u.String()))) defer os.Remove(conf3) o3, err := ProcessConfigFile(conf3) if err != nil { t.Fatalf("Error processing config file: %v", err) } if len(o3.LeafNode.Remotes) == 0 { t.Fatal("Expected at least a single leaf remote") } got = o3.LeafNode.Remotes[0].TLSTimeout expected = 10 if got != expected { t.Fatalf("Expected %v, got: %v", expected, got) } // Here we only process options without starting the server // and check the default for leafnode remotes. conf4 := createConfFile(t, []byte(fmt.Sprintf(` port: -1 leaf { remotes [ { url: "%s" tls { ca_file: "../test/configs/certs/tlsauth/ca.pem" } } ] } `, u.String()))) defer os.Remove(conf4) o4, err := ProcessConfigFile(conf4) if err != nil { t.Fatalf("Error processing config file: %v", err) } if len(o4.LeafNode.Remotes) == 0 { t.Fatal("Expected at least a single leaf remote") } got = o4.LeafNode.Remotes[0].TLSTimeout expected = float64(DEFAULT_LEAF_TLS_TIMEOUT) if int(got) != int(expected) { t.Fatalf("Expected %v, got: %v", expected, got) } }