// Copyright 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 ( "context" "crypto/tls" "encoding/json" "errors" "fmt" "io" "net/http" "os" "path/filepath" "testing" "time" "golang.org/x/crypto/ocsp" "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats.go" ) func newOCSPResponderRootCA(t *testing.T) *http.Server { t.Helper() respCertPEM := "configs/certs/ocsp_peer/mini-ca/caocsp/caocsp_cert.pem" respKeyPEM := "configs/certs/ocsp_peer/mini-ca/caocsp/private/caocsp_keypair.pem" issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" return newOCSPResponderDesignatedCustomAddress(t, issuerCertPEM, respCertPEM, respKeyPEM, "127.0.0.1:8888") } func newOCSPResponderIntermediateCA1(t *testing.T) *http.Server { t.Helper() respCertPEM := "configs/certs/ocsp_peer/mini-ca/ocsp1/ocsp1_bundle.pem" respKeyPEM := "configs/certs/ocsp_peer/mini-ca/ocsp1/private/ocsp1_keypair.pem" issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem" return newOCSPResponderDesignatedCustomAddress(t, issuerCertPEM, respCertPEM, respKeyPEM, "127.0.0.1:18888") } func newOCSPResponderIntermediateCA1Undelegated(t *testing.T) *http.Server { t.Helper() issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem" issuerCertKey := "configs/certs/ocsp_peer/mini-ca/intermediate1/private/intermediate1_keypair.pem" return newOCSPResponderCustomAddress(t, issuerCertPEM, issuerCertKey, "127.0.0.1:18888") } func newOCSPResponderBadDelegateIntermediateCA1(t *testing.T) *http.Server { t.Helper() // UserA2 is a cert issued by intermediate1, but intermediate1 did not add OCSP signing extension respCertPEM := "configs/certs/ocsp_peer/mini-ca/client1/UserA2_bundle.pem" respKeyPEM := "configs/certs/ocsp_peer/mini-ca/client1/private/UserA2_keypair.pem" issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem" return newOCSPResponderDesignatedCustomAddress(t, issuerCertPEM, respCertPEM, respKeyPEM, "127.0.0.1:18888") } func newOCSPResponderIntermediateCA2(t *testing.T) *http.Server { t.Helper() respCertPEM := "configs/certs/ocsp_peer/mini-ca/ocsp2/ocsp2_bundle.pem" respKeyPEM := "configs/certs/ocsp_peer/mini-ca/ocsp2/private/ocsp2_keypair.pem" issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/intermediate2/intermediate2_cert.pem" return newOCSPResponderDesignatedCustomAddress(t, issuerCertPEM, respCertPEM, respKeyPEM, "127.0.0.1:28888") } // TestOCSPPeerGoodClients is test of two NATS client (AIA enabled at leaf and cert) under good path (different intermediates) // and default ocsp_cache implementation and oscp_cache=false configuration func TestOCSPPeerGoodClients(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate2/intermediate2_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_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/client2/UserB1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Default cache: mTLS OCSP peer check on inbound client connection, client of intermediate CA 1", ` port: -1 # default ocsp_cache since omitted 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 # Long form configuration, non-default ca_timeout ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "Default cache: mTLS OCSP peer check on inbound client connection, client of intermediate CA 2", ` port: -1 # default ocsp_cache since omitted 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 # Short form configuration ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client2/UserB1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client2/private/UserB1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "Explicit true cache: mTLS OCSP peer check on inbound client connection, client of intermediate CA 1", ` port: -1 # Short form configuration ocsp_cache: true 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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") } }) } } // TestOCSPPeerUnknownClient is test of NATS client that is OCSP status Unknown from its OCSP Responder func TestOCSPPeerUnknownClient(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) defer intermediateCA1Responder.Shutdown(ctx) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Default cache, mTLS OCSP peer check on inbound client connection, client unknown to intermediate CA 1", ` port: -1 # Cache configuration is default 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 # Short form configuration ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() t.Errorf("Expected connection error, fell through") }) } } // TestOCSPPeerRevokedClient is test of NATS client that is OCSP status Revoked from its OCSP Responder func TestOCSPPeerRevokedClient(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Revoked) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "mTLS OCSP peer check on inbound client connection, client revoked by intermediate CA 1", ` port: -1 # Cache configuration is default 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 # Turn on CA OCSP check so this revoked client should NOT be able to connect ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, { "Explicit disable, mTLS OCSP peer check on inbound client connection, client revoked by intermediate CA 1 but no OCSP check", ` port: -1 # Cache configuration is default 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 # Explicit disable of OCSP peer check ocsp_peer: false } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "Implicit disable, mTLS OCSP peer check on inbound client connection, client revoked by intermediate CA 1 but no OCSP check", ` port: -1 # Cache configuration is default 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 # Implicit disable of OCSP peer check (i.e. not configured) # ocsp_peer: false } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "Explicit disable (long form), mTLS OCSP peer check on inbound client connection, client revoked by intermediate CA 1 but no OCSP check", ` port: -1 # Cache configuration is default 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 # Explicit disable of OCSP peer check, long form ocsp_peer: { verify: false } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerUnknownAndRevokedIntermediate test of NATS client that is OCSP good but either its intermediate is unknown or revoked func TestOCSPPeerUnknownAndRevokedIntermediate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Revoked) // No test OCSP status set on intermediate2, so unknown intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_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/client2/UserB1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "mTLS OCSP peer check on inbound client connection, client's intermediate is revoked", ` port: -1 # Cache configuration is default 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 # Short form configuration ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, { "mTLS OCSP peer check on inbound client connection, client's intermediate is unknown'", ` port: -1 # Cache configuration is default 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 # Short form configuration ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client2/UserB1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client2/private/UserB1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() t.Errorf("Expected connection error, fell through") }) } } // TestOCSPPeerLeafGood tests Leaf Spoke peer checking Leaf Hub, Leaf Hub peer checking Leaf Spoke, and both peer checking func TestOCSPPeerLeafGood(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) 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) for _, test := range []struct { name string hubconfig string spokeconfig string expected int }{ { "OCSP peer check on Leaf Hub by Leaf Spoke (TLS client OCSP verification of TLS server)", ` port: -1 # Cache configuration is default leaf: { listen: 127.0.0.1:7444 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 } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", tls: { ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 # Short form configuration ocsp_peer: true } } ] } `, 1, }, { "OCSP peer check on Leaf Spoke by Leaf Hub (TLS server OCSP verification of TLS client)", ` port: -1 # Cache configuration is default leaf: { listen: 127.0.0.1:7444 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 # Short form configuration ocsp_peer: true } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", 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 } } ] } `, 1, }, { "OCSP peer check bi-directionally", ` port: -1 # Cache configuration is default leaf: { listen: 127.0.0.1:7444 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 # Short form configuration ocsp_peer: true } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", 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 # Short form configuration ocsp_peer: true } } ] } `, 1, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(t, "") hubcontent := test.hubconfig hubconf := createConfFile(t, []byte(hubcontent)) hub, _ := RunServerWithConfig(hubconf) defer hub.Shutdown() spokecontent := test.spokeconfig spokeconf := createConfFile(t, []byte(spokecontent)) spoke, _ := RunServerWithConfig(spokeconf) defer spoke.Shutdown() checkLeafNodeConnectedCount(t, hub, test.expected) }) } } // TestOCSPPeerLeafRejects tests rejected Leaf Hub, rejected Leaf Spoke, and both rejecting each other func TestOCSPPeerLeafReject(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) 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.Revoked) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer2_cert.pem", ocsp.Revoked) for _, test := range []struct { name string hubconfig string spokeconfig string expected int }{ { "OCSP peer check on Leaf Hub by Leaf Spoke (TLS client OCSP verification of TLS server)", ` port: -1 # Cache configuration is default leaf: { listen: 127.0.0.1:7444 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 } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", tls: { ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem" timeout: 5 # Short form configuration ocsp_peer: true } } ] } `, 0, }, { "OCSP peer check on Leaf Spoke by Leaf Hub (TLS server OCSP verification of TLS client)", ` port: -1 leaf: { listen: 127.0.0.1:7444 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 # Short form configuration ocsp_peer: true } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", 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 } } ] } `, 0, }, { "OCSP peer check bi-directionally", ` port: -1 leaf: { listen: 127.0.0.1:7444 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 # Short form configuration ocsp_peer: true } } `, ` port: -1 leaf: { remotes: [ { url: "nats://127.0.0.1:7444", 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 # Short form configuration ocsp_peer: true } } ] } `, 0, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(t, "") hubcontent := test.hubconfig hubconf := createConfFile(t, []byte(hubcontent)) hub, _ := RunServerWithConfig(hubconf) defer hub.Shutdown() spokecontent := test.spokeconfig spokeconf := createConfFile(t, []byte(spokecontent)) spoke, _ := RunServerWithConfig(spokeconf) defer spoke.Shutdown() // Need to inject some time for leaf connection attempts to complete, could refine this to better // negative test time.Sleep(2000 * time.Millisecond) checkLeafNodeConnectedCount(t, hub, test.expected) }) } } func checkLeafNodeConnectedCount(t testing.TB, s *server.Server, lnCons int) { t.Helper() checkFor(t, 5*time.Second, 15*time.Millisecond, func() error { if nln := s.NumLeafNodes(); nln != lnCons { return fmt.Errorf("expected %d connected leafnode(s) for server %q, got %d", lnCons, s.ID(), nln) } return nil }) } // TestOCSPPeerGoodClientsNoneCache is test of two NATS client (AIA enabled at leaf and cert) under good path (different intermediates) // and ocsp cache type of none (no-op) func TestOCSPPeerGoodClientsNoneCache(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate2/intermediate2_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_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/client2/UserB1_cert.pem", ocsp.Good) deleteLocalStore(t, "") for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "None cache explicit long form: mTLS OCSP peer check on inbound client connection, client of intermediate CA 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } # Long form configuration ocsp_cache: { type: none } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "None cache explicit short form: mTLS OCSP peer check on inbound client connection, client of intermediate CA 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } # Short form configuration ocsp_cache: false `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { 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") } }) } } // TestOCSPPeerGoodClientsLocalCache is test of two NATS client (AIA enabled at leaf and cert) under good path (different intermediates) // and leveraging the local ocsp cache type func TestOCSPPeerGoodClientsLocalCache(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate2/intermediate2_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_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/client2/UserB1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Default cache, short form: mTLS OCSP peer check on inbound client connection, UserA1 client of intermediate CA 1", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } # Short form configuration, local as default ocsp_cache: true `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, { "Local cache long form: mTLS OCSP peer check on inbound client connection, UserB1 client of intermediate CA 2", ` port: -1 http_port: 8222 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 # Short form configuration ocsp_peer: true } # Long form configuration ocsp_cache: { type: local } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client2/UserB1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client2/private/UserB1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { // Cleanup any previous test that saved a local cache deleteLocalStore(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 } nc.Close() v := monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Misses != 2 || v.OCSPResponseCache.Responses != 2 { t.Errorf("Expected cache misses and cache items to be 2, got %d and %d", v.OCSPResponseCache.Misses, v.OCSPResponseCache.Responses) } // Should get a cache hit now 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") } v = monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Misses != 2 || v.OCSPResponseCache.Hits != 2 || v.OCSPResponseCache.Responses != 2 { t.Errorf("Expected cache misses, hits and cache items to be 2, got %d and %d and %d", v.OCSPResponseCache.Misses, v.OCSPResponseCache.Hits, v.OCSPResponseCache.Responses) } }) } } func TestOCSPPeerMonitor(t *testing.T) { for _, test := range []struct { name string config string NATSClient bool WSClient bool MQTTClient bool LeafClient bool LeafRemotes bool NumTrueLeafRemotes int }{ { "Monitor peer config setting on NATS client", ` port: -1 http_port: 8222 # Default cache configuration 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 # Long form configuration ocsp_peer: { verify: true } } `, true, false, false, false, false, 0, }, { "Monitor peer config setting on Websockets client", ` port: -1 http_port: 8222 # Default cache configuration websocket: { port: 8443 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 # Long form configuration ocsp_peer: { verify: true } } } `, false, true, false, false, false, 0, }, { "Monitor peer config setting on MQTT client", ` port: -1 http_port: 8222 # Default cache configuration # Required for MQTT server_name: "my_mqtt_server" jetstream: { enabled: true } mqtt: { port: 1883 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 # Long form configuration ocsp_peer: { verify: true } } } `, false, false, true, false, false, 0, }, { "Monitor peer config setting on Leaf client", ` port: -1 http_port: 8222 # Default cache configuration leaf: { port: 7422 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 # Long form configuration ocsp_peer: { verify: true } } } `, false, false, false, true, false, 0, }, { "Monitor peer config on some Leaf Remotes as well as Leaf client", ` port: -1 http_port: 8222 # Default cache configuration leaf: { port: 7422 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 # Long form configuration ocsp_peer: { verify: true } } remotes: [ { url: "nats-leaf://bogus:7422" 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 # Long form configuration ocsp_peer: { verify: true } } }, { url: "nats-leaf://anotherbogus:7422" 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 # Short form configuration ocsp_peer: true } }, { url: "nats-leaf://yetanotherbogus:7422" 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 # Peer not configured (default false) } } ] } `, false, false, false, true, true, 2, }, } { t.Run(test.name, func(t *testing.T) { content := test.config conf := createConfFile(t, []byte(content)) s, _ := RunServerWithConfig(conf) defer s.Shutdown() v := monitorGetVarzHelper(t, 8222) if test.NATSClient { if !v.TLSOCSPPeerVerify { t.Fatalf("Expected NATS Client TLSOCSPPeerVerify to be true, got false") } } if test.WSClient { if !v.Websocket.TLSOCSPPeerVerify { t.Fatalf("Expected WS Client TLSOCSPPeerVerify to be true, got false") } } if test.LeafClient { if !v.LeafNode.TLSOCSPPeerVerify { t.Fatalf("Expected Leaf Client TLSOCSPPeerVerify to be true, got false") } } if test.LeafRemotes { cnt := 0 for _, r := range v.LeafNode.Remotes { if r.TLSOCSPPeerVerify { cnt++ } } if cnt != test.NumTrueLeafRemotes { t.Fatalf("Expected %d Leaf Remotes with TLSOCSPPeerVerify true, got %d", test.NumTrueLeafRemotes, cnt) } } }) } } func TestOCSPResponseCacheMonitor(t *testing.T) { for _, test := range []struct { name string config string expect string }{ { "Monitor local cache enabled, explicit cache true", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true } } # Short form configuration ocsp_cache: true `, "local", }, { "Monitor local cache enabled, explicit cache type local", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true } } # Long form configuration ocsp_cache: { type: local } `, "local", }, { "Monitor local cache enabled, implicit default", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true } } # Short form configuration # ocsp_cache: true `, "local", }, { "Monitor none cache enabled, explicit cache false (short)", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true } } # Short form configuration ocsp_cache: false `, "", }, { "Monitor none cache enabled, explicit cache false (long)", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true } } # Long form configuration ocsp_cache: { type: none } `, "", }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(t, "") content := test.config conf := createConfFile(t, []byte(content)) s, _ := RunServerWithConfig(conf) defer s.Shutdown() v := monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Type != test.expect { t.Fatalf("Expected OCSP Response Cache to be %s, got %s", test.expect, v.OCSPResponseCache.Type) } }) } } func TestOCSPResponseCacheChangeAndReload(t *testing.T) { deleteLocalStore(t, "") // Start with ocsp cache set to none content := ` port: -1 http_port: 8222 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 # Short form configuration ocsp_peer: true } # Long form configuration ocsp_cache: { type: none } ` conf := createConfFile(t, []byte(content)) s, _ := RunServerWithConfig(conf) defer s.Shutdown() v := monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Type != "" { t.Fatalf("Expected OCSP Response Cache to have empty type in varz indicating none") } // Change to local cache content = ` port: -1 http_port: 8222 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 # Short form configuration ocsp_peer: true } # Long form configuration ocsp_cache: { type: local } ` 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) v = monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Type != "local" { t.Fatalf("Expected OCSP Response Cache type to be local, got %q", v.OCSPResponseCache.Type) } } func deleteLocalStore(t *testing.T, dir string) { t.Helper() if dir == "" { // default dir = "_rc_" } if err := os.RemoveAll(dir); err != nil { t.Fatalf("Error cleaning up local store: %v", err) } } func monitorGetVarzHelper(t *testing.T, httpPort int) *server.Varz { t.Helper() url := fmt.Sprintf("http://127.0.0.1:%d/", httpPort) resp, err := http.Get(url + "varz") if err != nil { t.Fatalf("Expected no error: Got %v\n", err) } if resp.StatusCode != 200 { t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Got an error reading the body: %v\n", err) } v := server.Varz{} if err := json.Unmarshal(body, &v); err != nil { t.Fatalf("Got an error unmarshalling the body: %v\n", err) } return &v } func writeCacheFile(dir string, content []byte) error { if dir == "" { dir = "_rc_" } err := os.MkdirAll(filepath.Join(dir), os.ModePerm) if err != nil { return err } return os.WriteFile(filepath.Join(dir, "cache.json"), content, os.ModePerm) } // TestOCSPPeerPreserveRevokedCacheItem is test of the preserve_revoked cache policy func TestOCSPPeerPreserveRevokedCacheItem(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option responses int64 revokes int64 goods int64 unknowns int64 err error rerr error clean bool }{ { "Test expired revoked cert not actually deleted", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check so this revoked client should NOT be able to connect ocsp_peer: { verify: true ca_timeout: 0.5 } } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, 1, 1, 0, 0, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), true, }, { "Test expired revoked cert replaced by current good cert", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check so this revoked client should NOT be able to connect ocsp_peer: true } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, 2, 0, 2, 0, nil, nil, false, }, } { t.Run(test.name, func(t *testing.T) { var intermediateCA1Responder *http.Server // clean slate starting the test and start the leaf CA responder for first run if test.clean { deleteLocalStore(t, "") // establish the revoked item (expired) in cache c := []byte(` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-05-29T17:56:45Z", "resp_status": "revoked", "resp_expires": "2023-05-29T17:56:49Z", "resp": "/wYAAFMyc1R3TwBSBQBao1Qr1QzUMIIGUQoBAKCCBkowggZGBgkrBgEFBQcwAQEEggY3MIIGMzCB46FZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HQDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA1MjkxNzU2MDBaMHUwczBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBc2ZAAQNDVaoBE2dwD0QQE0OVowDQYJKoZIhvcNAQELBQADggEBAGAax/vkv3SBFNbxp2utc/N6Rje4E0ceC972sWgqYjzYrH0oc/acg+OAXaxUjwqoQWaT+dHaI4D5qoTkMx7XlWATjI2L72IUTf6Luo92jPzyDFwb10CdeFHtRtEYD54Qbi/nD4oxQ8cSoLKC3wft2l3E/mK/1I4Mxwq15CioK4MhfzTISoeGZbjDXPKgloJOG3rn9v64vFGV6dosbLgaXEs+MPcCsPQYkwhOOyazuewRmIDOBp5QSsKPhqsT8Rs20t8LGTMkvjZniFWJs90l9QL9F1m3obq5nyuxrGt+7Rf5zoj4T+0XCOGtE+b7cRCLg43tFuTbaAQG8Z+qkPzpza+gggQ1MIIEMTCCBC0wggMVoAMCAQICFCnhUo39pSqH6x3kHUds4YpYaXOrOj8BBDBaUSLaLwIIGjAYSS+oEUludGVybWVkaWF0ZSBDQSAxMB4XDTIzMDUwMTE5MjgzOVoXDTMzMDQyOA0PUasVAEkMMIIBIi4nAgABBQD0QAEPADCCAQoCggEBAKMMyuuA66EOHnGb07P5Zc5wwiEGPDHBBn6lqErhIaN0VJ9XzlDWwyk8Q7CdPlSU7o36DXFs316eATB5bLuXXa+7WwV3cp9V5mZF9OLCz3sOWNYUanYprOMwKA3uvcqqrh8e70Dzw6sX8tfsDeH7aJoJg5kRWEKU+A3Umm+fO+hW8Km3GBqRQXxD49uxAfGtCznXZZjmFbAXqVZu+4R6wMxndfz2dYQxeMVtUY/QGdMWT4fvWzO5et3+X6hq/URUAPOkplv9O2U4T4JPucS9yZpW/FTxWC/L7vQI/bfsrSgIZpv4eJgy27FW3Q4xusbjVvUCL/t2KLvEi/Nr2qodOCECAwEAAaOB7TCB6jAdBgNVHQ4EFgQUy15QYHqrL6k7HiSrAkKN7IFgSBMwHwYDVR0jBBgwFoBSyQNQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQ4YBAMCB4AwFgEeACUBEBAMMAoGCIm0sAMJMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly8xMjcuMC4wLjE6MTg4ODgvaV1WKDFfY3JsLmRlcjAzEUscAQEEJzAlMCMRWwwwAYYXWkoALhICkTnw/0hlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`) err := writeCacheFile("", c) if err != nil { t.Fatal(err) } } else { intermediateCA1Responder = newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) defer intermediateCA1Responder.Shutdown(ctx) } 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() v := monitorGetVarzHelper(t, 8222) responses := v.OCSPResponseCache.Responses revokes := v.OCSPResponseCache.Revokes goods := v.OCSPResponseCache.Goods unknowns := v.OCSPResponseCache.Unknowns if !(responses == test.responses && revokes == test.revokes && goods == test.goods && unknowns == test.unknowns) { t.Fatalf("Expected %d response, %d revoked, %d good, %d unknown; got [%d] and [%d] and [%d] and [%d]", test.responses, test.revokes, test.goods, test.unknowns, responses, revokes, goods, unknowns) } }) } } // TestOCSPStapleFeatureInterop is a test of a NATS client (AIA enabled at leaf and cert) connecting to a NATS Server // in which both ocsp_peer is enabled on NATS client connections (verify client) and the ocsp staple is enabled such // that the NATS Server will staple its own OCSP response and make available to the NATS client during handshake. func TestOCSPStapleFeatureInterop(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) 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) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Interop: Both Good: mTLS OCSP peer check on inbound client connection and server's OCSP staple validated at client", ` port: -1 ocsp_cache: true 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 verify: true # Long form configuration, non-default ca_timeout ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("expected OCSP staple to be present") } resp, err := ocsp.ParseResponse(s.OCSPResponse, s.VerifiedChains[0][1]) if err != nil || resp.Status != ocsp.Good { return fmt.Errorf("expected a valid GOOD stapled response") } return nil }, }), nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() { setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) }, }, { "Interop: Bad Client: mTLS OCSP peer check on inbound client connection and server's OCSP staple validated at client", ` port: -1 ocsp_cache: true 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 verify: true # Long form configuration, non-default ca_timeout ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } `, []nats.Option{ nats.Secure(&tls.Config{ VerifyConnection: func(s tls.ConnectionState) error { if s.OCSPResponse == nil { return fmt.Errorf("expected OCSP staple to be present") } resp, err := ocsp.ParseResponse(s.OCSPResponse, s.VerifiedChains[0][1]) if err != nil || resp.Status != ocsp.Good { return fmt.Errorf("expected a valid GOOD stapled response") } return nil }, }), nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, fmt.Errorf("remote error: tls: bad certificate"), nil, func() { setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Revoked) }, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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") } }) } } // TestOCSPPeerWarnOnlyOption is test of NATS client that is OCSP Revoked status but allowed to pass with warn_only option func TestOCSPPeerWarnOnlyOption(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Revoked) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Revoked NATS client with warn_only explicitly set to false", ` port: -1 # Cache configuration is default 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 # Enable OCSP peer but with warn_only option set to false ocsp_peer: { verify: true warn_only: false } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, { "Revoked NATS client with warn_only explicitly set to true", ` port: -1 # Cache configuration is default 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 # Enable OCSP peer but with warn_only option set to true ocsp_peer: { verify: true warn_only: true } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerUnknownIsGoodOption is test of NATS client that is OCSP status Unknown from its OCSP Responder but we treat // status Unknown as "Good" func TestOCSPPeerUnknownIsGoodOption(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) defer intermediateCA1Responder.Shutdown(ctx) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "Unknown NATS client with no unknown_is_good option set (default false)", ` port: -1 # Cache configuration is default 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 # Short form configuration ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, { "Unknown NATS client with unknown_is_good set to true", ` port: -1 # Cache configuration is default 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 # Long form configuration ocsp_peer: { verify: true unknown_is_good: true } } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerAllowWhenCAUnreachableOption is test of the allow_when_ca_unreachable peer option func TestOCSPPeerAllowWhenCAUnreachableOption(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option cachedResponse string err error rerr error }{ { "Expired Revoked response in cache for UserA1 -- should be rejected connection (expired revoke honored)", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, ` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-05-29T17:56:45Z", "resp_status": "revoked", "resp_expires": "2023-05-29T17:56:49Z", "resp": "/wYAAFMyc1R3TwBSBQBao1Qr1QzUMIIGUQoBAKCCBkowggZGBgkrBgEFBQcwAQEEggY3MIIGMzCB46FZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HQDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA1MjkxNzU2MDBaMHUwczBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBc2ZAAQNDVaoBE2dwD0QQE0OVowDQYJKoZIhvcNAQELBQADggEBAGAax/vkv3SBFNbxp2utc/N6Rje4E0ceC972sWgqYjzYrH0oc/acg+OAXaxUjwqoQWaT+dHaI4D5qoTkMx7XlWATjI2L72IUTf6Luo92jPzyDFwb10CdeFHtRtEYD54Qbi/nD4oxQ8cSoLKC3wft2l3E/mK/1I4Mxwq15CioK4MhfzTISoeGZbjDXPKgloJOG3rn9v64vFGV6dosbLgaXEs+MPcCsPQYkwhOOyazuewRmIDOBp5QSsKPhqsT8Rs20t8LGTMkvjZniFWJs90l9QL9F1m3obq5nyuxrGt+7Rf5zoj4T+0XCOGtE+b7cRCLg43tFuTbaAQG8Z+qkPzpza+gggQ1MIIEMTCCBC0wggMVoAMCAQICFCnhUo39pSqH6x3kHUds4YpYaXOrOj8BBDBaUSLaLwIIGjAYSS+oEUludGVybWVkaWF0ZSBDQSAxMB4XDTIzMDUwMTE5MjgzOVoXDTMzMDQyOA0PUasVAEkMMIIBIi4nAgABBQD0QAEPADCCAQoCggEBAKMMyuuA66EOHnGb07P5Zc5wwiEGPDHBBn6lqErhIaN0VJ9XzlDWwyk8Q7CdPlSU7o36DXFs316eATB5bLuXXa+7WwV3cp9V5mZF9OLCz3sOWNYUanYprOMwKA3uvcqqrh8e70Dzw6sX8tfsDeH7aJoJg5kRWEKU+A3Umm+fO+hW8Km3GBqRQXxD49uxAfGtCznXZZjmFbAXqVZu+4R6wMxndfz2dYQxeMVtUY/QGdMWT4fvWzO5et3+X6hq/URUAPOkplv9O2U4T4JPucS9yZpW/FTxWC/L7vQI/bfsrSgIZpv4eJgy27FW3Q4xusbjVvUCL/t2KLvEi/Nr2qodOCECAwEAAaOB7TCB6jAdBgNVHQ4EFgQUy15QYHqrL6k7HiSrAkKN7IFgSBMwHwYDVR0jBBgwFoBSyQNQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQ4YBAMCB4AwFgEeACUBEBAMMAoGCIm0sAMJMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly8xMjcuMC4wLjE6MTg4ODgvaV1WKDFfY3JsLmRlcjAzEUscAQEEJzAlMCMRWwwwAYYXWkoALhICkTnw/0hlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), }, { "Expired Good response in cache for UserA1 -- should be allowed connection (cached item irrelevant)", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, ` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-06-05T16:33:52Z", "resp_status": "good", "resp_expires": "2023-06-05T16:33:55Z", "resp": "/wYAAFMyc1R3TwBYBQBgpzMn1wzUMIIGUwoBAKCCBkwwggZIBgkrBgEFBQcwAQEEggY5MIIGNTCB5aFZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HYDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA2MDUxNjMzMDBaMHcwdTBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBeAADZmABA1MVqgEToTAPRAATVaMA0GCSqGSIb3DQEBCwUAA4IBAQB9csJxA2VcpQYmC5lzI0dJJnEC1zcaP1oFbBm5VsZAbWh4mIGTqyEjMkucBqANTypnhWFRYcZE5nJE8tN8Aen0EBYkhk1wfEIhincaF3zs6x5RzigxQOqTPAanO55keKH5exYnfBcyCwAQiBbQaTXLJJdjerfqiRjqgsnLW8yl2IC8+kxTCfR4GHTK34mvqVWYYhP/xbaxTLJTp35t1vf1o78ct9R+z8W5sCSO4TsMNnExZlu4Ejeon/KivMR22j7nelTEaDCuaOl03WxKh9yhw2ix8V7lvR74wh5f4fjLZria6Y0+lUjwvvrZyBRwA62W/ihe3778q8KhLECFPQSaoIIENTCCBDEwggQtMIIDFaADAgECAhQp4VKN/aUqh+sd5B1HbOGKWGlzqzo/AQQwWlEk2jECCBowGEkxqBFJbnRlcm1lZGlhdGUgQ0EgMTAeFw0yMzA1MDExOTI4MzlaFw0zMzA0MjgND1GtFQBJDDCCASIuJwIAAQUA9EABDwAwggEKAoIBAQCjDMrrgOuhDh5xm9Oz+WXOcMIhBjwxwQZ+pahK4SGjdFSfV85Q1sMpPEOwnT5UlO6N+g1xbN9engEweWy7l12vu1sFd3KfVeZmRfTiws97DljWFGp2KazjMCgN7r3Kqq4fHu9A88OrF/LX7A3h+2iaCYOZEVhClPgN1JpvnzvoVvCptxgakUF8Q+PbsQHxrQs512WY5hWwF6lWbvuEesDMZ3X89nWEMXjFbVGP0BnTFk+H71szuXrd/l+oav1EVADzpKZb/TtlOE+CT7nEvcmaVvxU8Vgvy+70CP237K0oCGab+HiYMtuxVt0OMbrG41b1Ai/7dii7xIvza9qqHTghAgMBAAGjge0wgeowHQYDVR0OBBYEFMteUGB6qy+pOx4kqwJCjeyBYEgTMB8GA1UdIwQYMBaAUssDUDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEOGAQDAgeAMBYBHgAlARAQDDAKBgiJtrADCTA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vMTI3LjAuMC4xOjE4ODg4L2ldVigxX2NybC5kZXIwMxFLHAEBBCcwJTAjEVsMMAGGF1pKAC4SAgALBQD0AQEBAEhlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`, nil, nil, }, { "Expired Unknown response in cache for UserA1 -- should be allowed connection (cached item irrelevant)", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, ` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-06-05T16:45:01Z", "resp_status": "unknown", "resp_expires": "2023-06-05T16:45:05Z", "resp": "/wYAAFMyc1R3TwBSBQBH1aW01wzUMIIGUwoBAKCCBkwwggZIBgkrBgEFBQcwAQEEggY5MIIGNTCB5aFZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HYDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA2MDUxNjQ1MDBaMHcwdTBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBeCADpmAAwxWqAROhMA9EABNVowDQYJKoZIhvcNAQELBQADggEBAAzwED88ngiFj3hLzIejZ8DE/ppticX0vgAjUM8oXjDjwNXpSQ5xdXHsDk4RAYAVpNyiAfL2fapwz91g3JuZot6npp8smtZC5D0YMKK8iMjrx2aMqVyv+ai/33WG8PRWpBNzSTYaLhlBFjhUrx8HDu97ozNbmfgDWzRS1LqkJRa5YXvkyppqYTFSX73DV9R9tOVOwZ0x5WEKst9IJ+88mXsOBGyuye2Gh9RK6KsLgaOwiD9FBf18WRKIixeVM1Y/xHc/iwFPi8k3Z6hZ6gHX8NboQ/djCyzYVSWsUedTo/62uuagHPRWoYci4HQl4bSFXfcEO/EkWGnqkWBHfYZ4soigggQ1MIIEMTCCBC0wggMVoAMCAQICFCnhUo39pSqH6x3kHUds4YpYaXOrOj8BBDBaUSTaMQIIGjAYSTGoEUludGVybWVkaWF0ZSBDQSAxMB4XDTIzMDUwMTE5MjgzOVoXDTMzMDQyOA0PUa0VAEkMMIIBIi4nAgABBQD0QAEPADCCAQoCggEBAKMMyuuA66EOHnGb07P5Zc5wwiEGPDHBBn6lqErhIaN0VJ9XzlDWwyk8Q7CdPlSU7o36DXFs316eATB5bLuXXa+7WwV3cp9V5mZF9OLCz3sOWNYUanYprOMwKA3uvcqqrh8e70Dzw6sX8tfsDeH7aJoJg5kRWEKU+A3Umm+fO+hW8Km3GBqRQXxD49uxAfGtCznXZZjmFbAXqVZu+4R6wMxndfz2dYQxeMVtUY/QGdMWT4fvWzO5et3+X6hq/URUAPOkplv9O2U4T4JPucS9yZpW/FTxWC/L7vQI/bfsrSgIZpv4eJgy27FW3Q4xusbjVvUCL/t2KLvEi/Nr2qodOCECAwEAAaOB7TCB6jAdBgNVHQ4EFgQUy15QYHqrL6k7HiSrAkKN7IFgSBMwHwYDVR0jBBgwFoBSywNQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQ4YBAMCB4AwFgEeACUBEBAMMAoGCIm2sAMJMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly8xMjcuMC4wLjE6MTg4ODgvaV1WKDFfY3JsLmRlcjAzEUscAQEEJzAlMCMRWwwwAYYXWkoALhICkTnw/0hlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`, nil, nil, }, { "No response in cache for UserA1 -- should be allowed connection", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, "", nil, nil, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(t, "") c := []byte(test.cachedResponse) err := writeCacheFile("", c) if err != nil { t.Fatal(err) } 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() }) } } // TestOCSPResponseCacheLocalStoreOption is test of default and non-default local_store option func TestOCSPResponseCacheLocalStoreOpt(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option cachedResponse string err error rerr error storeLocation string }{ { "Test load from non-default local store _custom_; connect will reject only if cache file found and loaded", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local local_store: "_custom_" preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, ` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-05-29T17:56:45Z", "resp_status": "revoked", "resp_expires": "2023-05-29T17:56:49Z", "resp": "/wYAAFMyc1R3TwBSBQBao1Qr1QzUMIIGUQoBAKCCBkowggZGBgkrBgEFBQcwAQEEggY3MIIGMzCB46FZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HQDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA1MjkxNzU2MDBaMHUwczBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBc2ZAAQNDVaoBE2dwD0QQE0OVowDQYJKoZIhvcNAQELBQADggEBAGAax/vkv3SBFNbxp2utc/N6Rje4E0ceC972sWgqYjzYrH0oc/acg+OAXaxUjwqoQWaT+dHaI4D5qoTkMx7XlWATjI2L72IUTf6Luo92jPzyDFwb10CdeFHtRtEYD54Qbi/nD4oxQ8cSoLKC3wft2l3E/mK/1I4Mxwq15CioK4MhfzTISoeGZbjDXPKgloJOG3rn9v64vFGV6dosbLgaXEs+MPcCsPQYkwhOOyazuewRmIDOBp5QSsKPhqsT8Rs20t8LGTMkvjZniFWJs90l9QL9F1m3obq5nyuxrGt+7Rf5zoj4T+0XCOGtE+b7cRCLg43tFuTbaAQG8Z+qkPzpza+gggQ1MIIEMTCCBC0wggMVoAMCAQICFCnhUo39pSqH6x3kHUds4YpYaXOrOj8BBDBaUSLaLwIIGjAYSS+oEUludGVybWVkaWF0ZSBDQSAxMB4XDTIzMDUwMTE5MjgzOVoXDTMzMDQyOA0PUasVAEkMMIIBIi4nAgABBQD0QAEPADCCAQoCggEBAKMMyuuA66EOHnGb07P5Zc5wwiEGPDHBBn6lqErhIaN0VJ9XzlDWwyk8Q7CdPlSU7o36DXFs316eATB5bLuXXa+7WwV3cp9V5mZF9OLCz3sOWNYUanYprOMwKA3uvcqqrh8e70Dzw6sX8tfsDeH7aJoJg5kRWEKU+A3Umm+fO+hW8Km3GBqRQXxD49uxAfGtCznXZZjmFbAXqVZu+4R6wMxndfz2dYQxeMVtUY/QGdMWT4fvWzO5et3+X6hq/URUAPOkplv9O2U4T4JPucS9yZpW/FTxWC/L7vQI/bfsrSgIZpv4eJgy27FW3Q4xusbjVvUCL/t2KLvEi/Nr2qodOCECAwEAAaOB7TCB6jAdBgNVHQ4EFgQUy15QYHqrL6k7HiSrAkKN7IFgSBMwHwYDVR0jBBgwFoBSyQNQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQ4YBAMCB4AwFgEeACUBEBAMMAoGCIm0sAMJMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly8xMjcuMC4wLjE6MTg4ODgvaV1WKDFfY3JsLmRlcjAzEUscAQEEJzAlMCMRWwwwAYYXWkoALhICkTnw/0hlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), "_custom_", }, { "Test load from default local store when \"\" set; connect will reject only if cache file found and loaded", ` port: -1 http_port: 8222 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 # Turn on CA OCSP check but allow when CA is unreachable ocsp_peer: { verify: true ca_timeout: 0.5 allow_when_ca_unreachable: true } } # preserve revoked true ocsp_cache: { type: local local_store: "" preserve_revoked: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, ` { "5xL/SuHl6JN0OmxrNMpzVMTA73JVYcRfGX8+HvJinEI=": { "subject": "CN=UserA1,O=Testnats,L=Tacoma,ST=WA,C=US", "cached_at": "2023-05-29T17:56:45Z", "resp_status": "revoked", "resp_expires": "2023-05-29T17:56:49Z", "resp": "/wYAAFMyc1R3TwBSBQBao1Qr1QzUMIIGUQoBAKCCBkowggZGBgkrBgEFBQcwAQEEggY3MIIGMzCB46FZMFcxCzAJBgNVBAYTAlVTEQ0gCAwCV0ExDzANAQ0wBwwGVGFjb21hMREwDwEROAoMCFRlc3RuYXRzMRcwFQET8HQDDA5PQ1NQIFJlc3BvbmRlchgPMjAyMzA1MjkxNzU2MDBaMHUwczBNMAkGBSsOAwIaBQAEFKgwn5fplwQy+DsulBg5SRpx0iaYBBS1kW5PZLcWhHb5tL6ZzmCVmBqOnQIUXKGv1Xy7Fu/Cx+ZT/JQa7SS7tBc2ZAAQNDVaoBE2dwD0QQE0OVowDQYJKoZIhvcNAQELBQADggEBAGAax/vkv3SBFNbxp2utc/N6Rje4E0ceC972sWgqYjzYrH0oc/acg+OAXaxUjwqoQWaT+dHaI4D5qoTkMx7XlWATjI2L72IUTf6Luo92jPzyDFwb10CdeFHtRtEYD54Qbi/nD4oxQ8cSoLKC3wft2l3E/mK/1I4Mxwq15CioK4MhfzTISoeGZbjDXPKgloJOG3rn9v64vFGV6dosbLgaXEs+MPcCsPQYkwhOOyazuewRmIDOBp5QSsKPhqsT8Rs20t8LGTMkvjZniFWJs90l9QL9F1m3obq5nyuxrGt+7Rf5zoj4T+0XCOGtE+b7cRCLg43tFuTbaAQG8Z+qkPzpza+gggQ1MIIEMTCCBC0wggMVoAMCAQICFCnhUo39pSqH6x3kHUds4YpYaXOrOj8BBDBaUSLaLwIIGjAYSS+oEUludGVybWVkaWF0ZSBDQSAxMB4XDTIzMDUwMTE5MjgzOVoXDTMzMDQyOA0PUasVAEkMMIIBIi4nAgABBQD0QAEPADCCAQoCggEBAKMMyuuA66EOHnGb07P5Zc5wwiEGPDHBBn6lqErhIaN0VJ9XzlDWwyk8Q7CdPlSU7o36DXFs316eATB5bLuXXa+7WwV3cp9V5mZF9OLCz3sOWNYUanYprOMwKA3uvcqqrh8e70Dzw6sX8tfsDeH7aJoJg5kRWEKU+A3Umm+fO+hW8Km3GBqRQXxD49uxAfGtCznXZZjmFbAXqVZu+4R6wMxndfz2dYQxeMVtUY/QGdMWT4fvWzO5et3+X6hq/URUAPOkplv9O2U4T4JPucS9yZpW/FTxWC/L7vQI/bfsrSgIZpv4eJgy27FW3Q4xusbjVvUCL/t2KLvEi/Nr2qodOCECAwEAAaOB7TCB6jAdBgNVHQ4EFgQUy15QYHqrL6k7HiSrAkKN7IFgSBMwHwYDVR0jBBgwFoBSyQNQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQ4YBAMCB4AwFgEeACUBEBAMMAoGCIm0sAMJMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly8xMjcuMC4wLjE6MTg4ODgvaV1WKDFfY3JsLmRlcjAzEUscAQEEJzAlMCMRWwwwAYYXWkoALhICkTnw/0hlzm2RRjA3tvJ2wELj9e7pMg5GtdWdrLDyI/U1qBxhZoHADbyku7W+R1iL8dFfc4PSmdo+owsygZakvahXjv49xJNX7wV3YMmIHC4lfurIlY2mSnPlu2zEOwEDkI0S9WkTxXmHrkXLSciQJDkwzye6MR5fW+APk4JmKDPc46Go/K1A0EgxY/ugahMYsYtZu++W+IOYbEoYNxoCrcJCHX4c3Ep3t/Wulz4X6DWWhaDkMMUDC2JVE8E/3xUbw0X3adZe9Xf8T+goOz7wLCAigXKj1hvRUmOGISIGelv0KsfluZesG1a1TGLp+W9JX0M9nOaFOvjJTDP96aqIjs8oXGk=" } }`, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), "_rc_", }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(t, test.storeLocation) c := []byte(test.cachedResponse) err := writeCacheFile(test.storeLocation, c) if err != nil { t.Fatal(err) } 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() }) } } // TestOCSPPeerIncrementalSaveLocalCache is test of timer-based response cache save as new entries added func TestOCSPPeerIncrementalSaveLocalCache(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate2/intermediate2_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_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/client2/UserB1_cert.pem", ocsp.Good) var fi os.FileInfo var err error for _, test := range []struct { name string config string opts [][]nats.Option err error rerr error configure func() }{ { "Default cache, short form: mTLS OCSP peer check on inbound client connection, UserA1 client of intermediate CA 1", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 30 } } # Local cache with custom save_interval for testability ocsp_cache: { type: local # Save if dirty ever 1 second save_interval: 1 } `, [][]nats.Option{ { nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, { nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client2/UserB1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client2/private/UserB1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { // Cleanup any previous test that saved a local cache deleteLocalStore(t, "") fi, err = statCacheFile("") if err != nil && fi != nil && fi.Size() != 0 { t.Fatalf("Expected no local cache file, got a FileInfo with size %d", fi.Size()) } test.configure() content := test.config conf := createConfFile(t, []byte(content)) s, opts := RunServerWithConfig(conf) defer s.Shutdown() // Connect with UserA1 client and get a CA Response nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts[0]...) 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 } nc.Close() time.Sleep(2 * time.Second) fi, err = statCacheFile("") if err == nil && fi != nil && fi.Size() > 0 { // good } else { if err != nil { t.Fatalf("Expected an extant local cache file, got error: %v", err) } if fi != nil { t.Fatalf("Expected non-zero size local cache file, got a FileInfo with size %d", fi.Size()) } } firstFi := fi // Connect with UserB1 client and get another CA Response nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts[1]...) 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 } nc.Close() time.Sleep(2 * time.Second) fi, err = statCacheFile("") if err == nil && fi != nil && fi.Size() > firstFi.Size() { // good } else { if err != nil { t.Fatalf("Expected an extant local cache file, got error: %v", err) } if fi != nil { t.Fatalf("Expected non-zero size local cache file with more bytes, got a FileInfo with size %d", fi.Size()) } } }) } } func statCacheFile(dir string) (os.FileInfo, error) { if dir == "" { dir = "_rc_" } return os.Stat(filepath.Join(dir, "cache.json")) } // TestOCSPPeerUndelegatedCAResponseSigner func TestOCSPPeerUndelegatedCAResponseSigner(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1Undelegated(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "mTLS OCSP peer check on inbound client connection, responder is CA (undelegated)", ` port: -1 # Cache configuration is default 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 # Turn on CA OCSP check so unvalidated clients can't connect ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerDelegatedCAResponseSigner func TestOCSPPeerDelegatedCAResponseSigner(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "mTLS OCSP peer check on inbound client connection, responder is CA (undelegated)", ` port: -1 # Cache configuration is default 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 # Turn on CA OCSP check so unvalidated clients can't connect ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerBadDelegatedCAResponseSigner func TestOCSPPeerBadDelegatedCAResponseSigner(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) intermediateCA1Responder := newOCSPResponderBadDelegateIntermediateCA1(t) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error configure func() }{ { "mTLS OCSP peer check on inbound client connection, responder is not a legal delegate", ` port: -1 # Cache configuration is default 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 # Turn on CA OCSP check so unvalidated clients can't connect ocsp_peer: true } `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, errors.New("remote error: tls: bad certificate"), errors.New("expect error"), func() {}, }, } { t.Run(test.name, func(t *testing.T) { deleteLocalStore(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() }) } } // TestOCSPPeerNextUpdateUnset is test of scenario when responder does not set NextUpdate and cache TTL option is used func TestOCSPPeerNextUpdateUnset(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() rootCAResponder := newOCSPResponderRootCA(t) rootCAResponderURL := fmt.Sprintf("http://%s", rootCAResponder.Addr) defer rootCAResponder.Shutdown(ctx) setOCSPStatus(t, rootCAResponderURL, "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem", ocsp.Good) respCertPEM := "configs/certs/ocsp_peer/mini-ca/ocsp1/ocsp1_bundle.pem" respKeyPEM := "configs/certs/ocsp_peer/mini-ca/ocsp1/private/ocsp1_keypair.pem" issuerCertPEM := "configs/certs/ocsp_peer/mini-ca/intermediate1/intermediate1_cert.pem" intermediateCA1Responder := newOCSPResponderBase(t, issuerCertPEM, respCertPEM, respKeyPEM, true, "127.0.0.1:18888", 0) intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr) defer intermediateCA1Responder.Shutdown(ctx) setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/client1/UserA1_cert.pem", ocsp.Good) for _, test := range []struct { name string config string opts []nats.Option err error rerr error expectedMisses int64 configure func() }{ { "TTL set to 4 seconds with second client connection leveraging cache from first client connect", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 0 cache_ttl_when_next_update_unset: 4 } } # Short form configuration, local as default ocsp_cache: true `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, 2, func() {}, }, { "TTL set to 1 seconds with second client connection not leveraging cache items from first client connect", ` port: -1 http_port: 8222 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 # Long form configuration ocsp_peer: { verify: true ca_timeout: 5 allowed_clockskew: 0 cache_ttl_when_next_update_unset: 1 } } # Short form configuration, local as default ocsp_cache: true `, []nats.Option{ nats.ClientCert("./configs/certs/ocsp_peer/mini-ca/client1/UserA1_bundle.pem", "./configs/certs/ocsp_peer/mini-ca/client1/private/UserA1_keypair.pem"), nats.RootCAs("./configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"), nats.ErrorHandler(noOpErrHandler), }, nil, nil, 3, func() {}, }, } { t.Run(test.name, func(t *testing.T) { // Cleanup any previous test that saved a local cache deleteLocalStore(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 } nc.Close() // Wait interval shorter than first test, and longer than second test time.Sleep(2 * time.Second) 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() v := monitorGetVarzHelper(t, 8222) if v.OCSPResponseCache.Misses != test.expectedMisses || v.OCSPResponseCache.Responses != 2 { t.Errorf("Expected cache misses to be %d and cache items to be 2, got %d and %d", test.expectedMisses, v.OCSPResponseCache.Misses, v.OCSPResponseCache.Responses) } }) } }