diff options
| author | James O'Doherty <james@theodohertyfamily.com> | 2026-05-22 10:22:40 -0400 |
|---|---|---|
| committer | James O'Doherty <james@theodohertyfamily.com> | 2026-05-22 10:22:40 -0400 |
| commit | 401683a6b11e5a7810c949147a12f2c4bbfba48a (patch) | |
| tree | 30e411bf7eefcb91e5f17921d42b63ba2f3a3fb5 /internal/namespace/namespace.go | |
| parent | 5dbc46f3c1c75bf922bcc1c3df342323c23c04ce (diff) | |
feat: add argument verification diagnostic and secure temp files for launcher
Diffstat (limited to 'internal/namespace/namespace.go')
| -rw-r--r-- | internal/namespace/namespace.go | 40 |
1 files changed, 29 insertions, 11 deletions
diff --git a/internal/namespace/namespace.go b/internal/namespace/namespace.go index 5e31b9d..1a09b78 100644 --- a/internal/namespace/namespace.go +++ b/internal/namespace/namespace.go @@ -2,10 +2,10 @@ package namespace import ( _ "embed" + "encoding/json" "fmt" "os" "os/exec" - "path/filepath" "syscall" ) @@ -55,6 +55,18 @@ func VerifyIsolation() (bool, string) { return true, "Isolated and root" } +// VerifyArguments prints the current process arguments as a JSON array. +// This is used for E2E testing to verify that argument splitting and +// shell injection are not occurring during the bootstrap loop. +func VerifyArguments(args []string) error { + out, err := json.Marshal(args) + if err != nil { + return fmt.Errorf("failed to marshal arguments: %w", err) + } + fmt.Println(string(out)) + return nil +} + // Bootstrap ensures the process is running in an isolated user and network namespace. // It writes the embedded C launcher to a temporary file and replaces the current process. func Bootstrap() error { @@ -67,20 +79,26 @@ func Bootstrap() error { return fmt.Errorf("failed to get executable path: %w", err) } - // 1. Determine a secure location for the launcher binary. - // We use /run/user/$UID if available, otherwise /tmp. - tmpDir := os.Getenv("XDG_RUNTIME_DIR") - if tmpDir == "" { - tmpDir = os.TempDir() + // 1. Create a secure temporary file for the launcher binary. + // os.CreateTemp ensures a unique, unpredictable filename and restrictive permissions. + tmpFile, err := os.CreateTemp("", "wg-wrap-launcher-") + if err != nil { + return fmt.Errorf("failed to create temp launcher file: %w", err) } + launcherPath := tmpFile.Name() - launcherPath := filepath.Join(tmpDir, "wg-wrap-launcher") - - // 2. Write the embedded launcher binary to disk. - // We use 0700 permissions to ensure only the current user can execute it. - if err := os.WriteFile(launcherPath, launcherBytes, 0700); err != nil { + // 2. Write the embedded launcher binary to the temp file. + if _, err := tmpFile.Write(launcherBytes); err != nil { + tmpFile.Close() return fmt.Errorf("failed to write launcher binary: %w", err) } + + // Ensure the binary is executable (0700) + if err := tmpFile.Chmod(0700); err != nil { + tmpFile.Close() + return fmt.Errorf("failed to set launcher permissions: %w", err) + } + tmpFile.Close() // 3. Prepare arguments for the launcher. // The launcher expects: launcher <command_to_run> [args...] |
