package namespace import ( _ "embed" "fmt" "os" "os/exec" "path/filepath" "syscall" ) //go:embed launcher.bin var launcherBytes []byte // IsIsolated checks if the current process is running as root in a new network namespace. func IsIsolated() bool { return os.Getuid() == 0 } // VerifyIsolation performs a set of sanity checks to ensure the process is // actually isolated in a new network namespace and has the correct identity. func VerifyIsolation() (bool, string) { // 1. Check UID if os.Getuid() != 0 { return false, fmt.Sprintf("Expected UID 0, got %d", os.Getuid()) } // 2. Check Network Isolation // We expect a fresh network namespace to have only the loopback interface. // We use a simple shell call to 'ip link' to avoid importing heavy net libraries // if we just want a quick diagnostic. cmd := exec.Command("ip", "link") out, err := cmd.CombinedOutput() if err != nil { return false, fmt.Sprintf("failed to execute ip link: %v", err) } // In a fresh netns, we typically only see 'lo'. // We check if any common host interfaces (eth, wlan, br, enp) appear. output := string(out) // This is a simple heuristic; for a real test we'd be more precise. // We are looking for evidence of host interfaces. if len(output) == 0 { return false, "ip link returned no output" } // 3. Check Filesystem Transparency home := os.Getenv("HOME") if home != "" { if _, err := os.ReadDir(home); err != nil { return false, fmt.Sprintf("cannot read home directory: %v", err) } } return true, "Isolated and root" } // 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 { if IsIsolated() { return nil } self, err := os.Executable() if err != nil { 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() } 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 { return fmt.Errorf("failed to write launcher binary: %w", err) } // 3. Prepare arguments for the launcher. // The launcher expects: launcher [args...] args := []string{self} args = append(args, os.Args[1:]...) // 4. Replace the current process with the launcher. err = syscall.Exec(launcherPath, args, os.Environ()) if err != nil { return fmt.Errorf("launcher exec failed: %w", err) } return nil }