package e2e import ( "bufio" "fmt" "os" "os/exec" "path/filepath" "strings" "testing" ) func TestNamespaceSharing(t *testing.T) { binaryPath, err := GetBinaryPath() if err != nil { t.Skipf("Skipping test: %v", err) } tmpRuntimeDir := t.TempDir() tmpConfigDir := t.TempDir() profile := "sharing-test" // Write a valid dummy profile so it doesn't run in bare isolation profilesDir := filepath.Join(tmpConfigDir, "wg-wrap", "profiles") if err := os.MkdirAll(profilesDir, 0755); err != nil { t.Fatal(err) } profileConfPath := filepath.Join(profilesDir, profile+".conf") conf := `[Interface] Address = 10.0.0.2/24 PrivateKey = 0000000000000000000000000000000000000000000000000000000000000000 DNS = 1.1.1.1 [Peer] PublicKey = 0000000000000000000000000000000000000000000000000000000000000000 AllowedIPs = 0.0.0.0/0 Endpoint = 1.1.1.1:51820 ` if err := os.WriteFile(profileConfPath, []byte(conf), 0644); err != nil { t.Fatal(err) } pidsDir := filepath.Join(tmpRuntimeDir, "profiles", profile, "pids") // Start Process A running a command that outputs its netns and sleeps cmdA := exec.Command(binaryPath, "--profile", profile, "--", "sh", "-c", "readlink /proc/self/ns/net && sleep 5") cmdA.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir), fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpConfigDir), ) outA, err := cmdA.StdoutPipe() if err != nil { t.Fatalf("Failed to create stdout pipe for Process A: %v", err) } if err := cmdA.Start(); err != nil { t.Fatalf("Failed to start Process A: %v", err) } defer func() { _ = cmdA.Process.Kill() }() // Wait for Process A to output its netns ID line by line var parsedNetnsA string scannerA := bufio.NewScanner(outA) for scannerA.Scan() { line := strings.TrimSpace(scannerA.Text()) if strings.HasPrefix(line, "net:[") { parsedNetnsA = line break } } if parsedNetnsA == "" { t.Fatalf("Failed to get netns ID from Process A") } // Wait for Process A's PID to be registered on the host waitForPids(t, pidsDir, 1) // Start Process B to check its netns ID cmdB := exec.Command(binaryPath, "--profile", profile, "--", "readlink", "/proc/self/ns/net") cmdB.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir), fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpConfigDir), ) outB, err := cmdB.CombinedOutput() if err != nil { t.Fatalf("Process B failed to execute: %v\nOutput: %s", err, string(outB)) } var parsedNetnsB string for _, line := range strings.Split(string(outB), "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "net:[") { parsedNetnsB = trimmed break } } if parsedNetnsB == "" { t.Fatalf("Invalid netns ID format from Process B: %q", string(outB)) } if parsedNetnsA != parsedNetnsB { t.Errorf("BUG: Process A and Process B do not share the same network namespace!\nProcess A: %s\nProcess B: %s", parsedNetnsA, parsedNetnsB) } }