snapshot of https://github.com/nats-io/nats-server.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
NATS Server Tests / Lint (push) Failing after 3s
NATS Server Tests / Build (Latest Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2022) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2025) (push) Has been skipped
NATS Server Tests / Test Stores (push) Has been skipped
NATS Server Tests / Test JetStream (push) Has been skipped
NATS Server Tests / Test Raft (push) Has been skipped
NATS Server Tests / Test JetStream Consumers (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 1 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 2 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 3 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 4 (push) Has been skipped
NATS Server Tests / Test JetStream Supercluster (push) Has been skipped
NATS Server Tests / Test No-Race 1 (push) Has been skipped
NATS Server Tests / Test No-Race 2 (push) Has been skipped
NATS Server Tests / Test MQTT (push) Has been skipped
NATS Server Tests / Test Message Tracing (push) Has been skipped
NATS Server Tests / Test JWT (push) Has been skipped
NATS Server Tests / Test Remaining Server Tests (push) Has been skipped
NATS Server Tests / Test Other Packages (push) Has been skipped
Vulnerability Scan / govulncheck (push) Failing after 12m2s
Some checks failed
NATS Server Tests / Lint (push) Failing after 3s
NATS Server Tests / Build (Latest Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2022) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2025) (push) Has been skipped
NATS Server Tests / Test Stores (push) Has been skipped
NATS Server Tests / Test JetStream (push) Has been skipped
NATS Server Tests / Test Raft (push) Has been skipped
NATS Server Tests / Test JetStream Consumers (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 1 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 2 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 3 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 4 (push) Has been skipped
NATS Server Tests / Test JetStream Supercluster (push) Has been skipped
NATS Server Tests / Test No-Race 1 (push) Has been skipped
NATS Server Tests / Test No-Race 2 (push) Has been skipped
NATS Server Tests / Test MQTT (push) Has been skipped
NATS Server Tests / Test Message Tracing (push) Has been skipped
NATS Server Tests / Test JWT (push) Has been skipped
NATS Server Tests / Test Remaining Server Tests (push) Has been skipped
NATS Server Tests / Test Other Packages (push) Has been skipped
Vulnerability Scan / govulncheck (push) Failing after 12m2s
This commit is contained in:
350
server/certstore_windows_test.go
Normal file
350
server/certstore_windows_test.go
Normal file
@@ -0,0 +1,350 @@
|
||||
// Copyright 2022-2025 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.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
func runPowershellScript(scriptFile string, args []string) error {
|
||||
psExec, _ := exec.LookPath("powershell.exe")
|
||||
|
||||
execArgs := []string{psExec, "-command", fmt.Sprintf("& '%s'", scriptFile)}
|
||||
if len(args) > 0 {
|
||||
execArgs = append(execArgs, args...)
|
||||
}
|
||||
|
||||
cmdImport := &exec.Cmd{
|
||||
Path: psExec,
|
||||
Args: execArgs,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
return cmdImport.Run()
|
||||
}
|
||||
|
||||
func runConfiguredLeaf(t *testing.T, hubPort int, certStore string, matchBy string, match string, caMatch string, expectedLeafCount int) {
|
||||
|
||||
// Fire up the leaf
|
||||
u, err := url.Parse(fmt.Sprintf("nats://localhost:%d", hubPort))
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing url: %v", err)
|
||||
}
|
||||
|
||||
configStr := fmt.Sprintf(`
|
||||
port: -1
|
||||
leaf {
|
||||
remotes [
|
||||
{
|
||||
url: "%s"
|
||||
tls {
|
||||
cert_store: "%s"
|
||||
cert_match_by: "%s"
|
||||
cert_match: "%s"
|
||||
ca_certs_match: %s
|
||||
|
||||
# Test settings that succeed should be equivalent to:
|
||||
# cert_file: "../test/configs/certs/tlsauth/client.pem"
|
||||
# key_file: "../test/configs/certs/tlsauth/client-key.pem"
|
||||
# ca_file: "../test/configs/certs/tlsauth/ca.pem"
|
||||
timeout: 5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`, u.String(), certStore, matchBy, match, caMatch)
|
||||
|
||||
leafConfig := createConfFile(t, []byte(configStr))
|
||||
defer removeFile(t, leafConfig)
|
||||
leafServer, _ := RunServerWithConfig(leafConfig)
|
||||
defer leafServer.Shutdown()
|
||||
|
||||
// After client verify, hub will match by SAN email, SAN dns, and Subject (in that order)
|
||||
// Our test client specifies Subject only so we should match on that...
|
||||
|
||||
// A little settle time
|
||||
time.Sleep(1 * time.Second)
|
||||
checkLeafNodeConnectedCount(t, leafServer, expectedLeafCount)
|
||||
}
|
||||
|
||||
// TestLeafTLSWindowsCertStore tests the topology of two NATS Servers connected as leaf and hub with authentication of
|
||||
// leaf to hub via mTLS with leaf's certificate and signing key provisioned in the Windows certificate store.
|
||||
func TestLeafTLSWindowsCertStore(t *testing.T) {
|
||||
|
||||
// Client Identity (client.pem)
|
||||
// Issuer: O = NATS CA, OU = NATS.io, CN = localhost
|
||||
// Subject: OU = NATS.io, CN = example.com
|
||||
|
||||
// Make sure windows cert store is reset to avoid conflict with other tests
|
||||
err := runPowershellScript("../test/configs/certs/tlsauth/certstore/delete-cert-from-store.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell cert delete to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Provision Windows cert store with client cert and secret
|
||||
err = runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-client.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
err = runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-ca.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision CA to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Fire up the hub
|
||||
hubConfig := createConfFile(t, []byte(`
|
||||
port: -1
|
||||
leaf {
|
||||
listen: "127.0.0.1:-1"
|
||||
tls {
|
||||
ca_file: "../test/configs/certs/tlsauth/ca.pem"
|
||||
cert_file: "../test/configs/certs/tlsauth/server.pem"
|
||||
key_file: "../test/configs/certs/tlsauth/server-key.pem"
|
||||
timeout: 5
|
||||
verify_and_map: true
|
||||
}
|
||||
}
|
||||
|
||||
accounts: {
|
||||
AcctA: {
|
||||
users: [ {user: "OU = NATS.io, CN = example.com"} ]
|
||||
},
|
||||
AcctB: {
|
||||
users: [ {user: UserB1} ]
|
||||
},
|
||||
SYS: {
|
||||
users: [ {user: System} ]
|
||||
}
|
||||
}
|
||||
system_account: "SYS"
|
||||
`))
|
||||
defer removeFile(t, hubConfig)
|
||||
hubServer, hubOptions := RunServerWithConfig(hubConfig)
|
||||
defer hubServer.Shutdown()
|
||||
|
||||
testCases := []struct {
|
||||
certStore string
|
||||
certMatchBy string
|
||||
certMatch string
|
||||
caCertsMatch string
|
||||
expectedLeafCount int
|
||||
}{
|
||||
// Test subject and issuer
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "\"NATS CA\"", 1},
|
||||
{"WindowsCurrentUser", "Issuer", "NATS CA", "\"NATS CA\"", 1},
|
||||
{"WindowsCurrentUser", "Issuer", "Frodo Baggins, Inc.", "\"NATS CA\"", 0},
|
||||
{"WindowsCurrentUser", "Thumbprint", "7e44f478114a2e29b98b00beb1b3687d8dc0e481", "\"NATS CA\"", 0},
|
||||
// Test CAs, NATS CA is valid, others are missing
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"NATS CA\"]", 1},
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"GlobalSign\"]", 0},
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"Missing NATS Cert\"]", 0},
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"NATS CA\", \"Missing NATS Cert1\"]", 1},
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"Missing Cert2\",\"NATS CA\"]", 1},
|
||||
{"WindowsCurrentUser", "Subject", "example.com", "[\"Missing, Cert3\",\"Missing NATS Cert4\"]", 0},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
testName := fmt.Sprintf("%s by %s match %s", tc.certStore, tc.certMatchBy, tc.certMatch)
|
||||
t.Run(fmt.Sprintf(testName, tc.certStore, tc.certMatchBy, tc.certMatch, tc.caCertsMatch), func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if tc.expectedLeafCount != 0 {
|
||||
t.Fatalf("did not expect panic: %s", testName)
|
||||
} else {
|
||||
if !strings.Contains(fmt.Sprintf("%v", r), "Error processing configuration file") {
|
||||
t.Fatalf("did not expect unknown panic: %s", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
runConfiguredLeaf(t, hubOptions.LeafNode.Port,
|
||||
tc.certStore, tc.certMatchBy, tc.certMatch,
|
||||
tc.caCertsMatch, tc.expectedLeafCount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestServerTLSWindowsCertStore tests the topology of a NATS server requiring TLS and gettings it own server
|
||||
// cert identity (as used when accepting NATS client connections and negotiating TLS) from Windows certificate store.
|
||||
func TestServerTLSWindowsCertStore(t *testing.T) {
|
||||
|
||||
// Server Identity (server.pem)
|
||||
// Issuer: O = NATS CA, OU = NATS.io, CN = localhost
|
||||
// Subject: OU = NATS.io Operators, CN = localhost
|
||||
|
||||
// Make sure windows cert store is reset to avoid conflict with other tests
|
||||
err := runPowershellScript("../test/configs/certs/tlsauth/certstore/delete-cert-from-store.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell cert delete to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Provision Windows cert store with server cert and secret
|
||||
err = runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-server.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
err = runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-ca.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision CA to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Fire up the server
|
||||
srvConfig := createConfFile(t, []byte(`
|
||||
listen: "localhost:-1"
|
||||
tls {
|
||||
cert_store: "WindowsCurrentUser"
|
||||
cert_match_by: "Subject"
|
||||
cert_match: "NATS.io Operators"
|
||||
ca_certs_match: ["NATS CA"]
|
||||
timeout: 5
|
||||
}
|
||||
`))
|
||||
defer removeFile(t, srvConfig)
|
||||
srvServer, _ := RunServerWithConfig(srvConfig)
|
||||
if srvServer == nil {
|
||||
t.Fatalf("expected to be able start server with cert store configuration")
|
||||
}
|
||||
defer srvServer.Shutdown()
|
||||
|
||||
testCases := []struct {
|
||||
clientCA string
|
||||
expect bool
|
||||
}{
|
||||
{"../test/configs/certs/tlsauth/ca.pem", true},
|
||||
{"../test/configs/certs/tlsauth/client.pem", false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("Client CA: %s", tc.clientCA), func(t *testing.T) {
|
||||
nc, _ := nats.Connect(srvServer.clientConnectURLs[0], nats.RootCAs(tc.clientCA))
|
||||
err := nc.Publish("foo", []byte("hello TLS server-authenticated server"))
|
||||
if (err != nil) == tc.expect {
|
||||
t.Fatalf("expected publish result %v to TLS authenticated server", tc.expect)
|
||||
}
|
||||
nc.Close()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
nc, _ = nats.Connect(srvServer.clientConnectURLs[0], nats.RootCAs(tc.clientCA))
|
||||
err = nc.Publish("foo", []byte("hello TLS server-authenticated server"))
|
||||
if (err != nil) == tc.expect {
|
||||
t.Fatalf("expected repeated connection result %v to TLS authenticated server", tc.expect)
|
||||
}
|
||||
nc.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestServerIgnoreExpiredCerts tests if the server skips expired certificates in configuration, and finds non-expired ones
|
||||
func TestServerIgnoreExpiredCerts(t *testing.T) {
|
||||
|
||||
// Server Identities: expired.pem; not-expired.pem
|
||||
// Issuer: OU = NATS.io, CN = localhost
|
||||
// Subject: OU = NATS.io Operators, CN = localhost
|
||||
|
||||
testCases := []struct {
|
||||
certFile string
|
||||
expect bool
|
||||
}{
|
||||
{"expired.p12", false},
|
||||
{"not-expired.p12", true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("Server certificate: %s", tc.certFile), func(t *testing.T) {
|
||||
// Make sure windows cert store is reset to avoid conflict with other tests
|
||||
err := runPowershellScript("../test/configs/certs/tlsauth/certstore/delete-cert-from-store.ps1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell cert delete to succeed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Provision Windows cert store with server cert and secret
|
||||
err = runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-server.ps1", []string{tc.certFile})
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision to succeed: %s", err.Error())
|
||||
}
|
||||
// Fire up the server
|
||||
srvConfig := createConfFile(t, []byte(`
|
||||
listen: "localhost:-1"
|
||||
tls {
|
||||
cert_store: "WindowsCurrentUser"
|
||||
cert_match_by: "Subject"
|
||||
cert_match: "NATS.io Operators"
|
||||
cert_match_skip_invalid: true
|
||||
timeout: 5
|
||||
}
|
||||
`))
|
||||
defer removeFile(t, srvConfig)
|
||||
cfg, _ := ProcessConfigFile(srvConfig)
|
||||
if (cfg != nil) == tc.expect {
|
||||
return
|
||||
}
|
||||
if tc.expect == false {
|
||||
t.Fatalf("expected server start to fail with expired certificate")
|
||||
} else {
|
||||
t.Fatalf("expected server to start with non expired certificate")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowsTLS12ECDSA(t *testing.T) {
|
||||
err := runPowershellScript("../test/configs/certs/tlsauth/certstore/import-p12-server.ps1", []string{"ecdsa_server.pfx"})
|
||||
if err != nil {
|
||||
t.Fatalf("expected powershell provision to succeed: %v", err)
|
||||
}
|
||||
|
||||
config := createConfFile(t, []byte(`
|
||||
listen: "localhost:-1"
|
||||
tls {
|
||||
cert_store: "WindowsCurrentUser"
|
||||
cert_match_by: "Thumbprint"
|
||||
cert_match: "4F8AF21756E5DBBD54619BBB6F3CC5D455ED4468"
|
||||
cert_match_skip_invalid: true
|
||||
timeout: 5
|
||||
}
|
||||
`))
|
||||
defer removeFile(t, config)
|
||||
|
||||
srv, _ := RunServerWithConfig(config)
|
||||
if srv == nil {
|
||||
t.Fatalf("expected to be able start server with cert store configuration")
|
||||
}
|
||||
defer srv.Shutdown()
|
||||
|
||||
for name, version := range map[string]uint16{
|
||||
"TLS 1.3": tls.VersionTLS13,
|
||||
"TLS 1.2": tls.VersionTLS12,
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tc := &tls.Config{MaxVersion: version, MinVersion: version, InsecureSkipVerify: true}
|
||||
|
||||
if _, err = nats.Connect(srv.clientConnectURLs[0], nats.Secure(tc)); err != nil {
|
||||
t.Fatalf("connection with %s: %v", name, err)
|
||||
}
|
||||
|
||||
t.Logf("successful connection with %s", name)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user