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()