From 7010768877c227c9410a06908e4cb3e54db403bd Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Sun, 7 Jun 2026 21:51:48 -0400 Subject: security: restrict permissions of imported WireGuard profiles to 0600 WireGuard profile files contain sensitive private keys. Previously, these files were written with 0644 permissions, making them world-readable. This commit changes the file mode to 0600 to ensure only the owner can read and write the profiles. - Updated `handleProfileImport` to use 0600 permissions. - Added tests to verify that imported profiles have the correct permissions. --- internal/cli/cli.go | 2 +- internal/cli/profile_test.go | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index a4b9a9a..d100d4f 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -324,7 +324,7 @@ func (a *App) handleProfileImport(srcPath string, name string) error { return fmt.Errorf("failed to read source file: %w", err) } - if err := os.WriteFile(destPath, data, 0644); err != nil { + if err := os.WriteFile(destPath, data, 0600); err != nil { return fmt.Errorf("failed to write profile to %s: %w", destPath, err) } diff --git a/internal/cli/profile_test.go b/internal/cli/profile_test.go index e08ffc5..d373e95 100644 --- a/internal/cli/profile_test.go +++ b/internal/cli/profile_test.go @@ -50,11 +50,19 @@ func TestProfileImport(t *testing.T) { t.Errorf("expected no error, got %v", err) } - // Verify the file was actually copied to derived name + // Verify the file was actually copied to derived name and has correct permissions destFile := filepath.Join(profilesDir, "source.conf") if _, err := os.Stat(destFile); os.IsNotExist(err) { t.Errorf("expected profile to be imported to %s", destFile) } + var info os.FileInfo + info, err = os.Stat(destFile) + if err != nil { + t.Fatalf("failed to stat imported profile: %v", err) + } + if info.Mode().Perm() != 0600 { + t.Errorf("expected imported profile to have permissions 0600, got %o", info.Mode().Perm()) + } // 2. Test importing with explicit name argument customName := "custom-vpn" @@ -70,6 +78,13 @@ func TestProfileImport(t *testing.T) { if _, err := os.Stat(destCustomFile); os.IsNotExist(err) { t.Errorf("expected profile to be imported to %s", destCustomFile) } + info, err = os.Stat(destCustomFile) + if err != nil { + t.Fatalf("failed to stat imported profile: %v", err) + } + if info.Mode().Perm() != 0600 { + t.Errorf("expected imported profile to have permissions 0600, got %o", info.Mode().Perm()) + } // 3. Test duplicate import (should fail) appDup := NewApp([]string{"wg-wrap", "profile", "import", srcFile, customName}) -- cgit v1.2.3