summaryrefslogtreecommitdiff
path: root/internal/namespace/namespace.go
diff options
context:
space:
mode:
authorJames O'Doherty <james@theodohertyfamily.com>2026-05-22 10:22:40 -0400
committerJames O'Doherty <james@theodohertyfamily.com>2026-05-22 10:22:40 -0400
commit401683a6b11e5a7810c949147a12f2c4bbfba48a (patch)
tree30e411bf7eefcb91e5f17921d42b63ba2f3a3fb5 /internal/namespace/namespace.go
parent5dbc46f3c1c75bf922bcc1c3df342323c23c04ce (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.go40
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...]