// Copyright 2021-2023 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 ( "bytes" "context" "crypto" "crypto/tls" "crypto/x509" "encoding/base64" "encoding/pem" "fmt" "io" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "testing" "time" "golang.org/x/crypto/ocsp" "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats.go" ) const ( defaultResponseTTL = 4 * time.Second defaultAddress = "127.0.0.1:8888" ) func TestOCSPAlwaysMustStapleAndShutdown(t *testing.T) { // Certs that have must staple will auto shutdown the server. const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" serverKey = "configs/certs/ocsp/server-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) opts := server.Options{} opts.Host = "127.0.0.1" opts.NoLog = true opts.NoSigs = true opts.MaxControlLine = 4096 opts.Port = -1 opts.TLSCert = serverCert opts.TLSKey = serverKey opts.TLSCaCert = caCert opts.TLSTimeout = 5 tcOpts := &server.TLSConfigOpts{ CertFile: opts.TLSCert, KeyFile: opts.TLSKey, CaFile: opts.TLSCaCert, Timeout: opts.TLSTimeout, } tlsConf, err := server.GenTLSConfig(tcOpts) if err != nil { t.Fatal(err) } opts.TLSConfig = tlsConf opts.OCSPConfig = &server.OCSPConfig{ Mode: server.OCSPModeAlways, OverrideURLs: []string{addr}, } srv := RunServer(&opts) defer srv.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { return fmt.Errorf("invalid staple") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // The server will shutdown because the server becomes revoked // and the policy is to always must-staple. The OCSP Responder // instructs the NATS Server to fetch OCSP Staples every 2 seconds. time.Sleep(2 * time.Second) setOCSPStatus(t, addr, serverCert, ocsp.Revoked) time.Sleep(2 * time.Second) // Should be connection refused since server will abort now. _, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nats.ErrNoServers { t.Errorf("Expected connection refused") } // Verify that the server finishes shutdown srv.WaitForShutdown() } func TestOCSPMustStapleShutdown(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-status-request-cert.pem" serverKey = "configs/certs/ocsp/server-status-request-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) opts := server.Options{} opts.Host = "127.0.0.1" opts.NoLog = true opts.NoSigs = true opts.MaxControlLine = 4096 opts.Port = -1 opts.TLSCert = serverCert opts.TLSKey = serverKey opts.TLSCaCert = caCert opts.TLSTimeout = 5 tlsConfigOpts := &server.TLSConfigOpts{ CertFile: opts.TLSCert, KeyFile: opts.TLSKey, CaFile: opts.TLSCaCert, Timeout: opts.TLSTimeout, } tlsConf, err := server.GenTLSConfig(tlsConfigOpts) if err != nil { t.Fatal(err) } opts.TLSConfig = tlsConf opts.OCSPConfig = &server.OCSPConfig{ Mode: server.OCSPModeMust, OverrideURLs: []string{addr}, } srv := RunServer(&opts) defer srv.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { return fmt.Errorf("invalid staple") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // The server will shutdown because the server becomes revoked // and the policy is to always must-staple. The OCSP Responder // instructs the NATS Server to fetch OCSP Staples every 2 seconds. time.Sleep(2 * time.Second) setOCSPStatus(t, addr, serverCert, ocsp.Revoked) time.Sleep(2 * time.Second) // Should be connection refused since server will abort now. _, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nats.ErrNoServers { t.Errorf("Expected connection refused") } } func TestOCSPMustStapleAutoDoesNotShutdown(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) content := ` port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { t.Errorf("Expected valid OCSP staple status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // The server will shutdown because the server becomes revoked // and the policy is to always must-staple. The OCSP Responder // instructs the NATS Server to fetch OCSP Staples every 2 seconds. time.Sleep(2 * time.Second) setOCSPStatus(t, addr, serverCert, ocsp.Revoked) time.Sleep(2 * time.Second) // Should not be connection refused, the client will continue running and // be served the stale OCSP staple instead. nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Revoked { t.Errorf("Expected revoked status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() } func TestOCSPAutoWithoutMustStapleDoesNotShutdownOnRevoke(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" serverKey = "configs/certs/ocsp/server-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) opts := server.Options{} opts.Host = "127.0.0.1" opts.NoLog = true opts.NoSigs = true opts.MaxControlLine = 4096 opts.Port = -1 opts.TLSCert = serverCert opts.TLSKey = serverKey opts.TLSCaCert = caCert opts.TLSTimeout = 5 tlsConfigOpts := &server.TLSConfigOpts{ CertFile: opts.TLSCert, KeyFile: opts.TLSKey, CaFile: opts.TLSCaCert, Timeout: opts.TLSTimeout, } tlsConf, err := server.GenTLSConfig(tlsConfigOpts) if err != nil { t.Fatal(err) } opts.TLSConfig = tlsConf opts.OCSPConfig = &server.OCSPConfig{ Mode: server.OCSPModeAuto, OverrideURLs: []string{addr}, } srv := RunServer(&opts) defer srv.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return fmt.Errorf("Unexpected OCSP staple for certificate") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // Revoke the client certificate, nothing will happens since does // not have MustStaple. time.Sleep(2 * time.Second) setOCSPStatus(t, addr, serverCert, ocsp.Revoked) time.Sleep(2 * time.Second) // Should not be connection refused since server will continue running. nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Errorf("Unexpected error: %s", err) } nc.Close() } func TestOCSPClient(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" serverKey = "configs/certs/ocsp/server-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) ocspURL := fmt.Sprintf("http://%s", ocspr.Addr) defer ocspr.Shutdown(ctx) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "OCSP Stapling makes server fail to boot if status is unknown", ` port: -1 # Enable OCSP stapling with policy to honor must staple if present. ocsp: true tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "OCSP Stapling ignored by default if server without must staple status", ` port: -1 ocsp: true tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "OCSP Stapling honored by default if server has must staple status", ` port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) }, }, { "OCSP Stapling can be disabled even if server has must staple status", ` port: -1 ocsp: false tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Revoked) }, }, } { t.Run(test.name, func(t *testing.T) { test.configure() content := test.config conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...) if test.err == nil && err != nil { t.Errorf("Expected to connect, got %v", err) } else if test.err != nil && err == nil { t.Errorf("Expected error on connect") } else if test.err != nil && err != nil { // Error on connect was expected if test.err.Error() != err.Error() { t.Errorf("Expected error %s, got: %s", test.err, err) } return } defer nc.Close() nc.Subscribe("ping", func(m *nats.Msg) { m.Respond([]byte("pong")) }) nc.Flush() _, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond) if test.rerr != nil && err == nil { t.Errorf("Expected error getting response") } else if test.rerr == nil && err != nil { t.Errorf("Expected response") } }) } } func TestOCSPReloadRotateTLSCertWithNoURL(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" updatedServerCert = "configs/certs/ocsp/server-status-request-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) content := ` port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { t.Errorf("Expected valid OCSP staple status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // Change the contents with another that will fail to get a staple // since it does not have an URL. content = ` port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-cert.pem" key_file: "configs/certs/ocsp/server-status-request-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } // Reload show warning because of cert missing OCSP Url so cannot be used // with OCSP stapling. if err := s.Reload(); err != nil { t.Fatal(err) } expectedErr := fmt.Errorf("missing OCSP response") // The server will not shutdown because the reload will fail. _, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { // The new certificate does not have OCSP Staples since // it could not fetch one from a OCSP server. if s.OCSPResponse == nil { return expectedErr } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != expectedErr { t.Fatalf("Unexpected error: %s", expectedErr) } } func TestOCSPReloadRotateTLSCertDisableMustStaple(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" updatedServerCert = "configs/certs/ocsp/server-status-request-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) storeDir := t.TempDir() originalContent := ` port: -1 store_dir: '%s' tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` content := fmt.Sprintf(originalContent, storeDir) conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() var staple []byte nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { staple = s.OCSPResponse resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { t.Errorf("Expected valid OCSP staple status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() files := []string{} err = filepath.Walk(storeDir+"/ocsp/", func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } files = append(files, path) return nil }) if err != nil { t.Fatal(err) } found := false for _, file := range files { data, err := os.ReadFile(file) if err != nil { t.Error(err) } if bytes.Equal(staple, data) { found = true } } if !found { t.Error("Could not find OCSP Staple") } // Change the contents with another that has OCSP Stapling disabled. updatedContent := ` port: -1 store_dir: '%s' tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` content = fmt.Sprintf(updatedContent, storeDir) if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := s.Reload(); err != nil { t.Fatal(err) } // The new certificate does not have must staple so they will be missing. time.Sleep(4 * time.Second) nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return fmt.Errorf("unexpected OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() // Re-enable OCSP Stapling content = fmt.Sprintf(originalContent, storeDir) if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := s.Reload(); err != nil { t.Fatal(err) } var newStaple []byte nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { newStaple = s.OCSPResponse resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { t.Errorf("Expected valid OCSP staple status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() // Confirm that it got a new staple. files = []string{} err = filepath.Walk(storeDir+"/ocsp/", func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } files = append(files, path) return nil }) if err != nil { t.Fatal(err) } found = false for _, file := range files { data, err := os.ReadFile(file) if err != nil { t.Error(err) } if bytes.Equal(newStaple, data) { found = true } } if !found { t.Error("Could not find OCSP Staple") } if bytes.Equal(staple, newStaple) { t.Error("Expected new OCSP Staple") } } func TestOCSPReloadRotateTLSCertEnableMustStaple(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) setOCSPStatus(t, addr, updatedServerCert, ocsp.Good) // Start without OCSP Stapling MustStaple content := ` port: -1 tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return fmt.Errorf("unexpected OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // Change the contents with another that has OCSP Stapling enabled. content = ` port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := s.Reload(); err != nil { t.Fatal(err) } // The new certificate does not have must staple so they will be missing. time.Sleep(2 * time.Second) nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { resp, err := getOCSPStatus(s) if err != nil { return err } if resp.Status != ocsp.Good { t.Errorf("Expected valid OCSP staple status") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() } func TestOCSPCluster(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() // Seed server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { name: AB host: "127.0.0.1" advertise: 127.0.0.1 port: -1 pool_size: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // The rest srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { name: AB host: "127.0.0.1" advertise: 127.0.0.1 port: -1 pool_size: -1 routes: [ nats://127.0.0.1:%d ] connect_retries: 30 tls { cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() checkClusterFormed(t, srvA, srvB) // Revoke the seed server cluster certificate, following servers will not be able to verify connection. setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked) // Original set of servers still can communicate to each other, even though the cert has been revoked. // NOTE: Should we unplug from the cluster in case our server is revoke and OCSP policy is always or must? checkClusterFormed(t, srvA, srvB) // Wait for seed server to notice that its certificate has been revoked, // so that new routes can't connect to it. time.Sleep(6 * time.Second) // Start another server against the seed server that has an invalid OCSP Staple srvConfC := ` host: "127.0.0.1" port: -1 server_name: "CCC" tls { cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { name: AB host: "127.0.0.1" advertise: 127.0.0.1 port: -1 pool_size: -1 routes: [ nats://127.0.0.1:%d ] connect_retries: 30 tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Cluster.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() cB, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() cC, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() // There should be no connectivity between the clients due to the revoked staple. _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Fatal(err) } cB.Flush() resp, err := cC.Request("foo", nil, 2*time.Second) if err == nil { t.Errorf("Unexpected success, response: %+v", resp) } resp, err = cC.Request("bar", nil, 2*time.Second) if err == nil { t.Errorf("Unexpected success, response: %+v", resp) } // Switch the certs from the seed server to new ones that are not revoked, // this should restart OCSP Stapling for the cluster routes. srvConfA = ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { port: -1 pool_size: -1 compression: "disabled" name: AB host: "127.0.0.1" advertise: 127.0.0.1 connect_retries: 30 tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := srvA.Reload(); err != nil { t.Fatal(err) } // Wait to get a new OCSP Staple. time.Sleep(10 * time.Second) checkClusterFormed(t, srvA, srvB, srvC) // Now clients connect to C can communicate with B and A. _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } } func TestOCSPLeaf(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() // LeafNode server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 # Leaf connection must present certs. verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // LeafNode that has the original as a remote and running // without OCSP Stapling for the leaf remote. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { # Cert without OCSP Stapling enabled is able to connect. cert_file: "configs/certs/ocsp/client-cert.pem" key_file: "configs/certs/ocsp/client-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ] } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() checkLeafNodeConnected(t, srvA) // Revoke the seed server cluster certificate, following servers will not be able to verify connection. setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked) // Original set of servers still can communicate to each other via leafnode, even though the staple // for the leaf server has been revoked. checkLeafNodeConnected(t, srvA) // Wait for seed server to notice that its certificate has been revoked. time.Sleep(6 * time.Second) // Start another server against the seed server that has an revoked OCSP Staple. srvConfC := ` host: "127.0.0.1" port: -1 server_name: "CCC" tls { cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ] } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.LeafNode.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() // There should be connectivity between the clients even if there is a revoked staple // from a leafnode connection. _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Fatal(err) } cB.Flush() _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("Expected success, got: %+v", err) } _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("Expected success, got: %+v", err) } // Switch the certs from the leafnode server to new ones that are not revoked, // this should restart OCSP Stapling for the leafnode server. srvConfA = ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := srvA.Reload(); err != nil { t.Fatal(err) } time.Sleep(4 * time.Second) // A <-> A _, err = cA.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // B <-> A _, err = cB.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // C <-> A _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // C <-> B via leafnode A _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } } func TestOCSPLeafNoVerify(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() // LeafNode server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" # for this test, explicitly disable compression because we do it # in RunServer but here we do a config reload... compression: off tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 # Leaf server does not require certs for clients. verify: false } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // LeafNode remote that will connect to A and will not present certs. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ] } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() checkLeafNodeConnected(t, srvA) // Revoke the seed server cluster certificate, following servers will not be able to verify connection. setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked) // Original set of servers still can communicate to each other, even though the cert has been revoked. checkLeafNodeConnected(t, srvA) // Wait for seed server to notice that its certificate has been revoked. time.Sleep(6 * time.Second) // Start another server against the seed server that has an revoked OCSP Staple. srvConfC := ` host: "127.0.0.1" port: -1 server_name: "CCC" tls { cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 timeout: 5 } } ] } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.LeafNode.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() // There should be connectivity between the clients even if there is a revoked staple // from a leafnode connection. _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Fatal(err) } cB.Flush() _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("Expected success, got: %+v", err) } _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("Expected success, got: %+v", err) } // Switch the certs from the leafnode server to new ones that are not revoked, // this should restart OCSP Stapling for the leafnode server. srvConfA = ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" compression: off tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := srvA.Reload(); err != nil { t.Fatal(err) } time.Sleep(4 * time.Second) // A <-> A _, err = cA.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // B <-> A _, err = cB.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // C <-> A _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } // C <-> B via leafnode A _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } } func TestOCSPLeafVerifyLeafRemote(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() // LeafNode server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // LeafNode remote that will connect to A and will not present certs. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ] } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, _ := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() // Should not have been able to connect. checkLeafNodeConnections(t, srvA, 0) } func TestOCSPLeafVerifyAndMapLeafRemote(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() // LeafNode server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify_and_map: true } store_dir: '%s' leafnodes { host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify_and_map: true } } accounts: { leaf: { users: [ {user: "C=US, ST=CA, L=San Francisco, O=Synadia, OU=nats.io, CN=localhost server-status-request-url-04"} ] } client: { users: [ {user: "C=US, ST=CA, L=San Francisco, O=Synadia, OU=nats.io, CN=localhost client"} ] } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // LeafNode remote that will connect to A and will not present certs. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' leafnodes { remotes: [ { url: "tls://127.0.0.1:%d" tls { cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ] } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, _ := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() checkLeafNodeConnections(t, srvA, 1) } func TestOCSPGateway(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() // Gateway server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: A host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // LeafNode that has the original as a remote. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: B host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{ name: "A" url: "nats://127.0.0.1:%d" tls { cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } }] tls { cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Gateway.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() waitForOutboundGateways(t, srvB, 1, 5*time.Second) // Revoke the seed server cluster certificate, following servers will not be able to verify connection. setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked) // Original set of servers still can communicate to each other, even though the cert has been revoked. waitForOutboundGateways(t, srvA, 1, 5*time.Second) waitForOutboundGateways(t, srvB, 1, 5*time.Second) // Wait for gateway A to notice that its certificate has been revoked, // so that new gateways can't connect to it. time.Sleep(6 * time.Second) // Start another server against the seed server that has an invalid OCSP Staple srvConfC := ` host: "127.0.0.1" port: -1 server_name: "CCC" tls { cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: C host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{name: "A", url: "nats://127.0.0.1:%d" }] tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() // There should be no connectivity between the clients due to the revoked staple. _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond(nil) }) if err != nil { t.Fatal(err) } cB.Flush() // Gateway C was not able to mesh with Gateway A because of the revoked OCSP staple // so these requests to A and B should fail. resp, err := cC.Request("foo", nil, 2*time.Second) if err == nil { t.Errorf("Unexpected success, response: %+v", resp) } // Make request to B resp, err = cC.Request("bar", nil, 2*time.Second) if err == nil { t.Errorf("Unexpected success, response: %+v", resp) } // Switch the certs from the seed server to new ones that are not revoked, // this should restart OCSP Stapling for the cluster routes. srvConfA = ` host: "127.0.0.1" port: -1 server_name: "AAA" tls { cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: A host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := srvA.Reload(); err != nil { t.Fatal(err) } time.Sleep(4 * time.Second) waitForOutboundGateways(t, srvA, 2, 5*time.Second) waitForOutboundGateways(t, srvB, 2, 5*time.Second) waitForOutboundGateways(t, srvC, 2, 5*time.Second) // Now clients connect to C can communicate with B and A. _, err = cC.Request("foo", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } _, err = cC.Request("bar", nil, 2*time.Second) if err != nil { t.Errorf("%v", err) } } func TestOCSPGatewayIntermediate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem", ocsp.Good) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer2_cert.pem", ocsp.Good) // Gateway server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "AAA" ocsp: { mode: always url: %s } gateway { name: A host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } } ` srvConfA = fmt.Sprintf(srvConfA, intermediateCA1ResponderURL) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() srvConfB := ` host: "127.0.0.1" port: -1 server_name: "BBB" ocsp: { mode: always url: %s } gateway { name: B host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{ name: "A" url: "nats://127.0.0.1:%d" }] tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer2_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer2_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } } ` srvConfB = fmt.Sprintf(srvConfB, intermediateCA1ResponderURL, optsA.Gateway.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsA.Port), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() waitForOutboundGateways(t, srvB, 1, 5*time.Second) cB, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsB.Port), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() } func TestOCSPCustomConfig(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" serverKey = "configs/certs/ocsp/server-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) ocspURL := fmt.Sprintf("http://%s", ocspr.Addr) defer ocspr.Shutdown(ctx) var ( errExpectedNoStaple = fmt.Errorf("expected no staple") errMissingStaple = fmt.Errorf("missing OCSP Staple from server") ) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "OCSP Stapling in auto mode makes server fail to boot if status is revoked", ` port: -1 ocsp { mode: auto } tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return errExpectedNoStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Revoked) }, }, { "OCSP Stapling must staple ignored if disabled with ocsp: false", ` port: -1 ocsp: false tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return errExpectedNoStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) }, }, { "OCSP Stapling must staple ignored if disabled with ocsp mode never", ` port: -1 ocsp: { mode: never } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return errExpectedNoStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) }, }, { "OCSP Stapling in always mode fetches a staple even if cert does not have one", ` port: -1 ocsp { mode: always url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "OCSP Stapling in must staple mode does not fetch staple if there is no must staple flag", ` port: -1 ocsp { mode: must url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return errExpectedNoStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "OCSP Stapling in must staple mode fetches staple if there is a must staple flag", ` port: -1 ocsp { mode: must url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) }, }, } { t.Run(test.name, func(t *testing.T) { test.configure() content := test.config conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...) if test.err == nil && err != nil { t.Errorf("Expected to connect, got %v", err) } else if test.err != nil && err == nil { t.Errorf("Expected error on connect") } else if test.err != nil && err != nil { // Error on connect was expected if test.err.Error() != err.Error() { t.Errorf("Expected error %s, got: %s", test.err, err) } return } defer nc.Close() nc.Subscribe("ping", func(m *nats.Msg) { m.Respond([]byte("pong")) }) nc.Flush() _, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond) if test.rerr != nil && err == nil { t.Errorf("Expected error getting response") } else if test.rerr == nil && err != nil { t.Errorf("Expected response") } }) } } func TestOCSPCustomConfigReloadDisable(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) setOCSPStatus(t, addr, updatedServerCert, ocsp.Good) // Start with server without OCSP Stapling MustStaple content := ` port: -1 ocsp: { mode: always, url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // Change and disable OCSP Stapling. content = ` port: -1 ocsp: { mode: never } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := s.Reload(); err != nil { t.Fatal(err) } // The new certificate has must staple but OCSP Stapling is disabled. time.Sleep(2 * time.Second) nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return fmt.Errorf("unexpected OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() } func TestOCSPCustomConfigReloadEnable(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" serverCert = "configs/certs/ocsp/server-cert.pem" updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, serverCert, ocsp.Good) setOCSPStatus(t, addr, updatedServerCert, ocsp.Good) // Start with server without OCSP Stapling MustStaple content := ` port: -1 ocsp: { mode: never, url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse != nil { return fmt.Errorf("unexpected OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer nc.Close() sub, err := nc.SubscribeSync("foo") if err != nil { t.Fatal(err) } nc.Publish("foo", []byte("hello world")) nc.Flush() _, err = sub.NextMsg(1 * time.Second) if err != nil { t.Fatal(err) } nc.Close() // Change and disable OCSP Stapling. content = ` port: -1 ocsp: { mode: always, url: "http://127.0.0.1:8888" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } ` if err := os.WriteFile(conf, []byte(content), 0666); err != nil { t.Fatalf("Error writing config: %v", err) } if err := s.Reload(); err != nil { t.Fatal(err) } time.Sleep(2 * time.Second) nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple!") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } nc.Close() } func newOCSPResponderCustomAddress(t *testing.T, issuerCertPEM, issuerKeyPEM string, addr string) *http.Server { t.Helper() return newOCSPResponderBase(t, issuerCertPEM, issuerCertPEM, issuerKeyPEM, false, addr, defaultResponseTTL) } func newOCSPResponder(t *testing.T, issuerCertPEM, issuerKeyPEM string) *http.Server { t.Helper() return newOCSPResponderBase(t, issuerCertPEM, issuerCertPEM, issuerKeyPEM, false, defaultAddress, defaultResponseTTL) } func newOCSPResponderDesignatedCustomAddress(t *testing.T, issuerCertPEM, respCertPEM, respKeyPEM string, addr string) *http.Server { t.Helper() return newOCSPResponderBase(t, issuerCertPEM, respCertPEM, respKeyPEM, true, addr, defaultResponseTTL) } func newOCSPResponderBase(t *testing.T, issuerCertPEM, respCertPEM, respKeyPEM string, embed bool, addr string, responseTTL time.Duration) *http.Server { t.Helper() var mu sync.Mutex status := make(map[string]int) issuerCert := parseCertPEM(t, issuerCertPEM) respCert := parseCertPEM(t, respCertPEM) respKey := parseKeyPEM(t, respKeyPEM) mux := http.NewServeMux() // The "/statuses/" endpoint is for directly setting a key-value pair in // the CA's status database. mux.HandleFunc("/statuses/", func(rw http.ResponseWriter, r *http.Request) { defer r.Body.Close() key := r.URL.Path[len("/statuses/"):] switch r.Method { case "GET": mu.Lock() n, ok := status[key] if !ok { n = ocsp.Unknown } mu.Unlock() fmt.Fprintf(rw, "%s %d", key, n) case "POST": data, err := io.ReadAll(r.Body) if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } n, err := strconv.Atoi(string(data)) if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } mu.Lock() status[key] = n mu.Unlock() fmt.Fprintf(rw, "%s %d", key, n) default: http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed) return } }) // The "/" endpoint is for normal OCSP requests. This actually parses an // OCSP status request and signs a response with a CA. Lightly based off: // https://www.ietf.org/rfc/rfc2560.txt mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed) return } reqData, err := base64.StdEncoding.DecodeString(r.URL.Path[1:]) if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } ocspReq, err := ocsp.ParseRequest(reqData) if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } mu.Lock() n, ok := status[ocspReq.SerialNumber.String()] if !ok { n = ocsp.Unknown } mu.Unlock() tmpl := ocsp.Response{ Status: n, SerialNumber: ocspReq.SerialNumber, ThisUpdate: time.Now(), } if responseTTL != 0 { tmpl.NextUpdate = tmpl.ThisUpdate.Add(responseTTL) } if embed { tmpl.Certificate = respCert } respData, err := ocsp.CreateResponse(issuerCert, respCert, tmpl, respKey) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } rw.Header().Set("Content-Type", "application/ocsp-response") rw.Header().Set("Content-Length", fmt.Sprint(len(respData))) fmt.Fprint(rw, string(respData)) }) srv := &http.Server{ Addr: addr, Handler: mux, } go srv.ListenAndServe() time.Sleep(1 * time.Second) return srv } func setOCSPStatus(t *testing.T, ocspURL, certPEM string, status int) { t.Helper() cert := parseCertPEM(t, certPEM) hc := &http.Client{Timeout: 10 * time.Second} resp, err := hc.Post( fmt.Sprintf("%s/statuses/%s", ocspURL, cert.SerialNumber), "", strings.NewReader(fmt.Sprint(status)), ) if err != nil { t.Fatal(err) } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("failed to read OCSP HTTP response body: %s", err) } if got, want := resp.Status, "200 OK"; got != want { t.Error(strings.TrimSpace(string(data))) t.Fatalf("unexpected OCSP HTTP set status, got %q, want %q", got, want) } } func parseCertPEM(t *testing.T, certPEM string) *x509.Certificate { t.Helper() block := parsePEM(t, certPEM) cert, err := x509.ParseCertificate(block.Bytes) if err != nil { t.Fatalf("failed to parse cert '%s': %s", certPEM, err) } return cert } func parseKeyPEM(t *testing.T, keyPEM string) crypto.Signer { t.Helper() block := parsePEM(t, keyPEM) key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { key, err = x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { t.Fatalf("failed to parse ikey %s: %s", keyPEM, err) } } keyc := key.(crypto.Signer) return keyc } func parsePEM(t *testing.T, pemPath string) *pem.Block { t.Helper() data, err := os.ReadFile(pemPath) if err != nil { t.Fatal(err) } block, _ := pem.Decode(data) if block == nil { t.Fatalf("failed to decode PEM %s", pemPath) } return block } func getOCSPStatus(s tls.ConnectionState) (*ocsp.Response, error) { if len(s.VerifiedChains) == 0 { return nil, fmt.Errorf("missing TLS verified chains") } chain := s.VerifiedChains[0] if got, want := len(chain), 2; got < want { return nil, fmt.Errorf("incomplete cert chain, got %d, want at least %d", got, want) } leaf, issuer := chain[0], chain[1] resp, err := ocsp.ParseResponseForCert(s.OCSPResponse, leaf, issuer) if err != nil { return nil, fmt.Errorf("failed to parse OCSP response: %w", err) } if err := resp.CheckSignatureFrom(issuer); err != nil { return resp, err } return resp, nil } func TestOCSPTLSConfigNoLeafSet(t *testing.T) { o := DefaultTestOptions o.HTTPHost = "127.0.0.1" o.HTTPSPort = -1 o.TLSConfig = &tls.Config{ServerName: "localhost"} cert, err := tls.LoadX509KeyPair("configs/certs/server-cert.pem", "configs/certs/server-key.pem") if err != nil { t.Fatalf("Got error reading certificates: %s", err) } o.TLSConfig.Certificates = []tls.Certificate{cert} s := RunServer(&o) s.Shutdown() } func TestOCSPSuperCluster(t *testing.T) { const ( caCert = "configs/certs/ocsp/ca-cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ocspr := newOCSPResponder(t, caCert, caKey) defer ocspr.Shutdown(ctx) addr := fmt.Sprintf("http://%s", ocspr.Addr) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good) setOCSPStatus(t, addr, "configs/certs/ocsp/server-cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() storeDirD := t.TempDir() // Gateway server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "A" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { name: A host: "127.0.0.1" advertise: 127.0.0.1 port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } gateway { name: A host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // Server that has the original as a cluster. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "B" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' cluster { name: A host: "127.0.0.1" advertise: 127.0.0.1 port: -1 routes: [ nats://127.0.0.1:%d ] tls { cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } } gateway { name: A host: "127.0.0.1" advertise: "127.0.0.1" port: -1 tls { cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() // Start another server that will make connect as a gateway to cluster A. srvConfC := ` host: "127.0.0.1" port: -1 server_name: "C" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: C host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{ name: "A", urls: ["nats://127.0.0.1:%d"] tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } }] tls { cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() // Check that server is connected to any server from the other cluster. checkClusterFormed(t, srvA, srvB) waitForOutboundGateways(t, srvC, 1, 5*time.Second) // Start one more server that will become another gateway. srvConfD := ` host: "127.0.0.1" port: -1 server_name: "D" ocsp { mode: "auto", url: "%s" } tls { cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 } store_dir: '%s' gateway { name: D host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{ name: "A", urls: ["nats://127.0.0.1:%d"] tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 }}, { name: "C", urls: ["nats://127.0.0.1:%d"] #################################################################### ## TEST NOTE: This cert does not have an OCSP Staple intentionally## #################################################################### tls { ca_file: "configs/certs/ocsp/ca-cert.pem" cert_file: "configs/certs/ocsp/server-cert.pem" key_file: "configs/certs/ocsp/server-key.pem" timeout: 5 }} ] tls { cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem" key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem" ca_file: "configs/certs/ocsp/ca-cert.pem" timeout: 5 verify: true } } ` srvConfD = fmt.Sprintf(srvConfD, addr, storeDirD, optsA.Gateway.Port, optsC.Gateway.Port) conf = createConfFile(t, []byte(srvConfD)) srvD, _ := RunServerWithConfig(conf) defer srvD.Shutdown() // There should be a single gateway here because one of the gateway connections does not have a OCSP staple. waitForOutboundGateways(t, srvD, 1, 10*time.Second) // Connect to cluster A using server B. cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() // Connects to cluster C using server C. cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond([]byte("From Server A")) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond([]byte("From Server B")) }) if err != nil { t.Fatal(err) } cB.Flush() // Confirm that a message from server C can flow back to server A via gateway.. var ( resp *nats.Msg lerr error ) for i := 0; i < 10; i++ { resp, lerr = cC.Request("foo", nil, 500*time.Millisecond) if lerr != nil { continue } got := string(resp.Data) expected := "From Server A" if got != expected { t.Fatalf("Expected %v, got: %v", expected, got) } // Make request to B resp, lerr = cC.Request("bar", nil, 500*time.Millisecond) if lerr != nil { continue } got = string(resp.Data) expected = "From Server B" if got != expected { t.Errorf("Expected %v, got: %v", expected, got) } lerr = nil break } if lerr != nil { t.Errorf("Unexpected error: %v", lerr) } if n := srvD.NumOutboundGateways(); n > 1 { t.Errorf("Expected single gateway, got: %v", n) } } func TestOCSPLocalIssuerDetermination(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) // Test constants ocspURL := intermediateCA1ResponderURL clientTrustBundle := "configs/certs/ocsp_peer/mini-ca/misc/trust_config1_bundle.pem" serverCert := "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem" var ( errMissingStaple = fmt.Errorf("missing OCSP Staple from server") ) for _, test := range []struct { name string config string opts []nats.Option err error rerr error serverStart bool configure func() }{ { "Correct issuer configured in cert bundle", ` port: -1 ocsp { mode: always } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(clientTrustBundle), nats.ErrorHandler(noOpErrHandler), }, nil, nil, true, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "Wrong issuer configured in cert bundle, server no start", ` port: -1 ocsp { mode: always } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/misc/misconfig_TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(clientTrustBundle), nats.ErrorHandler(noOpErrHandler), }, nil, nil, false, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "Issuer configured in CA bundle only, configuration 1", ` port: -1 ocsp { mode: always } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config1_bundle.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(clientTrustBundle), nats.ErrorHandler(noOpErrHandler), }, nil, nil, true, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "Issuer configured in CA bundle only, configuration 2", ` port: -1 ocsp { mode: always } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config2_bundle.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(clientTrustBundle), nats.ErrorHandler(noOpErrHandler), }, nil, nil, true, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, { "Issuer configured in CA bundle only, configuration 3", ` port: -1 ocsp { mode: always } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config3_bundle.pem" timeout: 5 } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return errMissingStaple } return nil }, }), nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"), nats.RootCAs(clientTrustBundle), nats.ErrorHandler(noOpErrHandler), }, nil, nil, true, func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) }, }, } { t.Run(test.name, func(t *testing.T) { defer func() { r := recover() if r != nil && test.serverStart { t.Fatalf("Expected server start, unexpected panic: %v", r) } if r == nil && !test.serverStart { t.Fatalf("Expected server to not start and panic thrown") } }() test.configure() content := test.config conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) // server may not start for some tests if s != nil { defer s.Shutdown() } nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...) if test.err == nil && err != nil { t.Errorf("Expected to connect, got %v", err) } else if test.err != nil && err == nil { t.Errorf("Expected error on connect") } else if test.err != nil && err != nil { // Error on connect was expected if test.err.Error() != err.Error() { t.Errorf("Expected error %s, got: %s", test.err, err) } return } defer nc.Close() nc.Subscribe("ping", func(m *nats.Msg) { m.Respond([]byte("pong")) }) nc.Flush() _, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond) if test.rerr != nil && err == nil { t.Errorf("Expected error getting response") } else if test.rerr == nil && err != nil { t.Errorf("Expected response") } }) } } func TestMixedCAOCSPSuperCluster(t *testing.T) { const ( caCert = "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" caKey = "configs/certs/ocsp/ca-key.pem" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem", ocsp.Good) intermediateCA2Responder := newOCSPResponderIntermediateCA2(t) intermediateCA2ResponderURL := fmt.Sprintf("http://%s", intermediateCA2Responder.Addr) defer intermediateCA2Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA2ResponderURL, "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_cert.pem", ocsp.Good) // Store Dirs storeDirA := t.TempDir() storeDirB := t.TempDir() storeDirC := t.TempDir() // Gateway server configuration srvConfA := ` host: "127.0.0.1" port: -1 server_name: "A" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } store_dir: '%s' cluster { name: A host: "127.0.0.1" advertise: 127.0.0.1 port: -1 tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } } gateway { name: A host: "127.0.0.1" port: -1 advertise: "127.0.0.1" tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 verify: true } } ` srvConfA = fmt.Sprintf(srvConfA, storeDirA) sconfA := createConfFile(t, []byte(srvConfA)) srvA, optsA := RunServerWithConfig(sconfA) defer srvA.Shutdown() // Server that has the original as a cluster. srvConfB := ` host: "127.0.0.1" port: -1 server_name: "B" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } store_dir: '%s' cluster { name: A host: "127.0.0.1" advertise: 127.0.0.1 port: -1 routes: [ nats://127.0.0.1:%d ] tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } } gateway { name: A host: "127.0.0.1" advertise: "127.0.0.1" port: -1 tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 verify: true } } ` srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port) conf := createConfFile(t, []byte(srvConfB)) srvB, optsB := RunServerWithConfig(conf) defer srvB.Shutdown() // Client connects to server A. cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cA.Close() // Start another server that will make connect as a gateway to cluster A but with different CA issuer. srvConfC := ` host: "127.0.0.1" port: -1 server_name: "C" ocsp { mode: "always" } tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } store_dir: '%s' gateway { name: C host: "127.0.0.1" advertise: "127.0.0.1" port: -1 gateways: [{ name: "A", urls: ["nats://127.0.0.1:%d"] tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 } }] tls { cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem" key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem" ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 verify: true } } ` srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port) conf = createConfFile(t, []byte(srvConfC)) srvC, optsC := RunServerWithConfig(conf) defer srvC.Shutdown() // Check that server is connected to any server from the other cluster. checkClusterFormed(t, srvA, srvB) waitForOutboundGateways(t, srvC, 1, 5*time.Second) // Connect to cluster A using server B. cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cB.Close() // Connects to cluster C using server C. cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port), nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("missing OCSP Staple from server") } return nil }, }), nats.RootCAs(caCert), nats.ErrorHandler(noOpErrHandler), ) if err != nil { t.Fatal(err) } defer cC.Close() _, err = cA.Subscribe("foo", func(m *nats.Msg) { m.Respond([]byte("From Server A")) }) if err != nil { t.Errorf("%v", err) } cA.Flush() _, err = cB.Subscribe("bar", func(m *nats.Msg) { m.Respond([]byte("From Server B")) }) if err != nil { t.Fatal(err) } cB.Flush() // Confirm that a message from server C can flow back to server A via gateway.. var ( resp *nats.Msg lerr error ) for i := 0; i < 10; i++ { resp, lerr = cC.Request("foo", nil, 500*time.Millisecond) if lerr != nil { continue } got := string(resp.Data) expected := "From Server A" if got != expected { t.Fatalf("Expected %v, got: %v", expected, got) } // Make request to B resp, lerr = cC.Request("bar", nil, 500*time.Millisecond) if lerr != nil { continue } got = string(resp.Data) expected = "From Server B" if got != expected { t.Errorf("Expected %v, got: %v", expected, got) } lerr = nil break } if lerr != nil { t.Errorf("Unexpected error: %v", lerr) } }