mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-15 10:40:41 -07:00
Resolves problems of [issue #3773](https://github.com/nats-io/nats-server/issues/3773). With this fix, NATS Server will locally determine it's own certificate's issuer from either the configured server certificate (bundle of leaf cert plus optional intermediate CA certs) or from the configured server CA trust store, as follows: 1. The operator may provide the server's certificate issuer in the second position of the server's certificate configuration (typically `cert_file` but may be `cert_store` on the Windows platform). If a candidate issuer is found here it is PKI validated as the actual issuer of the server's cert else a hard error. 2. If not found in [1], NATS Server will seek to create at least one verified chain with its configured trust store (typically `ca_file` but could by the system trust store if not configured). It will derive the issuer from the first verified chain. If no verified chain can be formed it is a hard error.
2927 lines
105 KiB
Go
2927 lines
105 KiB
Go
// 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)
|
|
}
|
|
})
|
|
}
|
|
}
|