From f51058964dacf52efd2150fe24db299db5925be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 04:40:53 +0100 Subject: [PATCH] fix: verify NKey file has secure permissions before reading Reject NKey files that are readable by group or others (permissions more permissive than 0600). This prevents accidental exposure of private keys through overly permissive file permissions. Co-Authored-By: Claude Opus 4.5 --- cmd/homelab-deploy/main.go | 2 +- internal/nats/client.go | 9 +++++++++ internal/nats/client_test.go | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/cmd/homelab-deploy/main.go b/cmd/homelab-deploy/main.go index f5f7bad..5f019ff 100644 --- a/cmd/homelab-deploy/main.go +++ b/cmd/homelab-deploy/main.go @@ -16,7 +16,7 @@ import ( "github.com/urfave/cli/v3" ) -const version = "0.1.0" +const version = "0.1.1" func main() { app := &cli.Command{ diff --git a/internal/nats/client.go b/internal/nats/client.go index dde956b..ff05ee3 100644 --- a/internal/nats/client.go +++ b/internal/nats/client.go @@ -25,6 +25,15 @@ type Client struct { // Connect establishes a connection to NATS using NKey authentication. func Connect(cfg Config) (*Client, error) { + // Verify NKey file has secure permissions (no group/other access) + info, err := os.Stat(cfg.NKeyFile) + if err != nil { + return nil, fmt.Errorf("failed to stat nkey file: %w", err) + } + if perm := info.Mode().Perm(); perm&0o077 != 0 { + return nil, fmt.Errorf("nkey file has insecure permissions %04o: must not be accessible by group or others", perm) + } + seed, err := os.ReadFile(cfg.NKeyFile) if err != nil { return nil, fmt.Errorf("failed to read nkey file: %w", err) diff --git a/internal/nats/client_test.go b/internal/nats/client_test.go index 203803a..1ab76a8 100644 --- a/internal/nats/client_test.go +++ b/internal/nats/client_test.go @@ -21,6 +21,29 @@ func TestConnect_InvalidNKeyFile(t *testing.T) { } } +func TestConnect_InsecureNKeyFilePermissions(t *testing.T) { + // Create a temp file with insecure permissions + tmpDir := t.TempDir() + keyFile := filepath.Join(tmpDir, "insecure.nkey") + if err := os.WriteFile(keyFile, []byte("test-content"), 0644); err != nil { + t.Fatalf("failed to write temp file: %v", err) + } + + cfg := Config{ + URL: "nats://localhost:4222", + NKeyFile: keyFile, + Name: "test", + } + + _, err := Connect(cfg) + if err == nil { + t.Error("expected error for insecure nkey file permissions") + } + if err != nil && !contains(err.Error(), "insecure permissions") { + t.Errorf("expected insecure permissions error, got: %v", err) + } +} + func TestConnect_InvalidNKeySeed(t *testing.T) { // Create a temp file with invalid content tmpDir := t.TempDir()