From d2173cdbc03884ecd9534e9369f8ebe1634f7e9c Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Fri, 29 May 2026 21:07:46 -0400 Subject: feat: harden bootstrap and optimize network data path - Security: Eliminate namespace escape risk by removing `HostBind` and enforcing `FDBind` using pre-opened host socket FDs. - Security: Replace unsafe `atoi` with `strtol` and strict validation in the C launcher to prevent malformed PID joins. - Stability: Fix PID wraparound by storing session timestamps in PID files to detect recycled PIDs. - Stability: Resolve DNS mount leaks by implementing proper unmounting of `/etc/resolv.conf` during tunnel shutdown. - Performance: Optimize `FDBind` throughput by implementing batch packet processing in the receive loop. - Deployment: Implement `memfd_create` for the C launcher to support `noexec` temporary directories and reduce disk I/O. - Maintenance: Replace external `ip` CLI dependency with native `netlink` library for robust network configuration. - Quality: Fix all `golangci-lint` errors and replace remaining panics with explicit error handling. --- internal/namespace/namespace.go | 42 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) (limited to 'internal/namespace/namespace.go') diff --git a/internal/namespace/namespace.go b/internal/namespace/namespace.go index 6f56a84..54414a9 100644 --- a/internal/namespace/namespace.go +++ b/internal/namespace/namespace.go @@ -68,7 +68,8 @@ func VerifyArguments(args []string) error { } // 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. +// It uses memfd_create to run the embedded C launcher from memory, bypassing +// disk-based noexec restrictions. func Bootstrap() (err error) { if IsIsolated() { return nil @@ -97,12 +98,11 @@ func Bootstrap() (err error) { return fmt.Errorf("failed to get executable path: %w", err) } - execFd, launcherPath, err := prepareLauncher() + execFd, err := prepareLauncher() if err != nil { return err } fdsToClose = append(fdsToClose, execFd) - _ = os.Remove(launcherPath) // Unlink early; fd remains valid // Clear close-on-exec if flags, err := unix.FcntlInt(uintptr(execFd), unix.F_GETFD, 0); err == nil { @@ -187,12 +187,11 @@ func BootstrapJoin(targetPid int) (err error) { return fmt.Errorf("failed to get executable path: %w", err) } - execFd, launcherPath, err := prepareLauncher() + execFd, err := prepareLauncher() if err != nil { return err } fdsToClose = append(fdsToClose, execFd) - _ = os.Remove(launcherPath) if flags, err := unix.FcntlInt(uintptr(execFd), unix.F_GETFD, 0); err == nil { _, _ = unix.FcntlInt(uintptr(execFd), unix.F_SETFD, flags&^unix.FD_CLOEXEC) @@ -222,33 +221,18 @@ func BootstrapJoin(targetPid int) (err error) { return nil } -func prepareLauncher() (int, string, error) { - tmpFile, err := os.CreateTemp("", "wg-wrap-launcher-") +func prepareLauncher() (int, error) { + // Use memfd_create to create an anonymous file in memory. + // This bypasses the need for a temporary disk file and avoids noexec restrictions. + fd, err := unix.MemfdCreate("wg-wrap-launcher", 0) if err != nil { - return 0, "", fmt.Errorf("failed to create temp launcher file: %w", err) + return 0, fmt.Errorf("failed to create memfd: %w", err) } - launcherPath := tmpFile.Name() - defer func() { - if err != nil { - _ = tmpFile.Close() - _ = os.Remove(launcherPath) - } - }() - - if _, err = tmpFile.Write(launcherBytes); err != nil { - return 0, "", fmt.Errorf("failed to write launcher binary: %w", err) - } - - if err = tmpFile.Chmod(0700); err != nil { - return 0, "", fmt.Errorf("failed to set launcher permissions: %w", err) - } - - execFd, err := syscall.Open(launcherPath, syscall.O_RDONLY, 0) - if err != nil { - return 0, "", fmt.Errorf("failed to open launcher for exec: %w", err) + if _, err = unix.Write(fd, launcherBytes); err != nil { + _ = unix.Close(fd) + return 0, fmt.Errorf("failed to write launcher binary to memfd: %w", err) } - _ = tmpFile.Close() - return execFd, launcherPath, nil + return fd, nil } -- cgit v1.2.3