From 284ed362550e1fccc62ecd876dbd3f4c8fc721e2 Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Fri, 29 May 2026 19:14:11 -0400 Subject: feat(dns): implement unprivileged DNS isolation, precedence order, and profile configuration Completed the remaining roadmap and documentation requirements by implementing robust unprivileged DNS management, completing the profile configuration subcommand, and resolving data-plane transition socket crashes. Detailed changes: - **DNS Isolation**: Implemented `ConfigureResolvConf` in `internal/wireguard/wireguard.go` to override `/etc/resolv.conf` within the unprivileged network/mount namespace. Transitioned the mount namespace to private propagation (`MS_PRIVATE`) and safely bind-mounted a temporary resolv.conf file over `/etc/resolv.conf` without mutating the host's configuration. - **DNS Precedence Order**: Integrated CLI flag `--dns-server`, parsed `.conf` interface DNS parameters, and added a safe default fallback (`1.1.1.1`) to ensure absolute host DNS leak prevention inside wrapped sessions. - **Socket Duplication in FDBind**: Resolved a lifecycle panic in `FDBind` where `wireguard-go` called `Close` and `Open` during device state transitions, causing "use of closed network connection" errors. Implemented file descriptor duplication using `unix.Dup` during bind initialization to gracefully persist the host-socket context across interface transitions and allow clean exit synchronization. - **Profile Configuration**: Implemented `handleProfileConfigure` in `internal/cli/cli.go` to launch the default system `$EDITOR` (falling back to `vi`) on a profile, satisfying the documentation's requirements. - **Hermetic Testing Polish**: - Created `dns_helpers.go` providing a `MockDNSServer` packet probe. - Added E2E tests for unprivileged DNS resolution, data-plane UDP handshake transmission, and 3-way DNS precedence routing. - Refactored `TestNamespaceLifecycleAutomation`, `TestConfigPropagation`, and `TestMTUFragmentation` to use default profile fallbacks, fixing failing stats on missing profiles. - Resolved all `golangci-lint` and `go fmt` warnings to maintain a completely clean static analysis pipeline. --- tests/e2e/lifecycle_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests/e2e/lifecycle_test.go') diff --git a/tests/e2e/lifecycle_test.go b/tests/e2e/lifecycle_test.go index 08887e1..0f6cae1 100644 --- a/tests/e2e/lifecycle_test.go +++ b/tests/e2e/lifecycle_test.go @@ -46,7 +46,7 @@ func TestNamespaceLifecycleAutomation(t *testing.T) { // 2. Override the runtime base dir to a temporary location tmpRuntimeDir := t.TempDir() - profile := "e2e-lifecycle-test" + profile := "default" pidsDir := filepath.Join(tmpRuntimeDir, "profiles", profile, "pids") // Clean up before starting @@ -56,7 +56,7 @@ func TestNamespaceLifecycleAutomation(t *testing.T) { t.Run("ReferenceCounting", func(t *testing.T) { // Start a process that exits quickly - cmd1 := exec.Command(binaryPath, "--profile", profile, "--", "sleep", "0.1") + cmd1 := exec.Command(binaryPath, "--profile", "default", "--", "sleep", "0.1") cmd1.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir)) if err := cmd1.Start(); err != nil { t.Fatalf("Failed to start cmd1: %v", err) @@ -66,7 +66,7 @@ func TestNamespaceLifecycleAutomation(t *testing.T) { waitForPids(t, pidsDir, 1) // Start a second process using the same profile - cmd2 := exec.Command(binaryPath, "--profile", profile, "--", "sleep", "0.1") + cmd2 := exec.Command(binaryPath, "--profile", "default", "--", "sleep", "0.1") cmd2.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir)) if err := cmd2.Start(); err != nil { t.Fatalf("Failed to start cmd2: %v", err) -- cgit v1.2.3