feat: enforce nats authentication (#123)

This commit is contained in:
Julian Sommer
2026-04-09 15:27:00 +10:00
committed by GitHub
parent 7449090c86
commit 553e7857df
3 changed files with 91 additions and 2 deletions
+1
View File
@@ -287,6 +287,7 @@ var viperblockStartCmd = &cobra.Command{
service, err := service.New("viperblock", &viperblockd.Config{
NatsHost: nodeConfig.NATS.Host,
NatsToken: nodeConfig.NATS.ACL.Token,
PluginPath: pluginPath,
S3Host: nodeConfig.Predastore.Host,
Bucket: nodeConfig.Predastore.Bucket,
+2 -2
View File
@@ -16,7 +16,7 @@ cluster {
routes = [
{{- range .ClusterRoutes }}
"nats://{{ . }}",
"nats-route://{{$.NatsToken}}@{{ . }}",
{{- end }}
]
}
@@ -26,7 +26,7 @@ server_name: spinifex-nats-{{.Node}}
# Authorization
authorization {
# token: "{{.NatsToken}}"
token: "{{.NatsToken}}"
}
# JetStream for persistent messaging and KV store
+88
View File
@@ -0,0 +1,88 @@
package nats
import (
"os"
"path/filepath"
"runtime"
"testing"
"text/template"
"time"
"github.com/nats-io/nats-server/v2/server"
"github.com/nats-io/nats.go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// templatePath returns the absolute path to the canonical nats.conf template
// used by admin init. Reading at test time (rather than embedding a copy)
// ensures the test always validates the real template.
func templatePath(t *testing.T) string {
t.Helper()
_, thisFile, _, ok := runtime.Caller(0)
require.True(t, ok)
return filepath.Join(filepath.Dir(thisFile), "..", "..", "..", "cmd", "spinifex", "cmd", "templates", "nats.conf")
}
// TestRenderedConfig_EnforcesAuth renders the production nats.conf template with
// a known token, starts an embedded NATS server from the resulting config, and
// verifies that unauthenticated and wrong-token connections are rejected while
// correct-token connections succeed.
func TestRenderedConfig_EnforcesAuth(t *testing.T) {
token := "nats_test-secret-token-1234"
tmpDir := t.TempDir()
confPath := filepath.Join(tmpDir, "nats.conf")
// Read and render the production template.
raw, err := os.ReadFile(templatePath(t))
require.NoError(t, err)
tmpl, err := template.New("nats.conf").Parse(string(raw))
require.NoError(t, err)
f, err := os.Create(confPath)
require.NoError(t, err)
err = tmpl.Execute(f, map[string]any{
"BindIP": "127.0.0.1",
"Node": "test-node",
"NatsToken": token,
"DataDir": tmpDir,
"LogDir": tmpDir,
})
f.Close()
require.NoError(t, err)
// Start NATS server from the rendered config.
opts, err := server.ProcessConfigFile(confPath)
require.NoError(t, err)
// Override for test isolation: random port, no monitoring.
opts.Port = -1
opts.LogFile = ""
opts.HTTPHost = ""
opts.HTTPPort = 0
ns, err := server.NewServer(opts)
require.NoError(t, err)
go ns.Start()
require.True(t, ns.ReadyForConnections(5*time.Second))
t.Cleanup(func() { ns.Shutdown() })
t.Run("no token rejected", func(t *testing.T) {
_, err := nats.Connect(ns.ClientURL(), nats.MaxReconnects(0))
assert.Error(t, err, "unauthenticated connection should be rejected")
})
t.Run("wrong token rejected", func(t *testing.T) {
_, err := nats.Connect(ns.ClientURL(), nats.Token("wrong-token"), nats.MaxReconnects(0))
assert.Error(t, err, "wrong token should be rejected")
})
t.Run("correct token accepted", func(t *testing.T) {
nc, err := nats.Connect(ns.ClientURL(), nats.Token(token), nats.MaxReconnects(0))
require.NoError(t, err, "correct token should be accepted")
defer nc.Close()
assert.True(t, nc.IsConnected())
})
}