From 6c21e008c3ab2fac6ccb17edfc1db8c32c469689 Mon Sep 17 00:00:00 2001 From: JY Tan Date: Fri, 26 Dec 2025 16:19:07 -0800 Subject: [PATCH] Handle library usage and missing network namespace gracefully --- internal/sandbox/linux.go | 8 +++++++- internal/sandbox/linux_features_stub.go | 1 + internal/sandbox/manager.go | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/sandbox/linux.go b/internal/sandbox/linux.go index 7ebb8f9..cecc529 100644 --- a/internal/sandbox/linux.go +++ b/internal/sandbox/linux.go @@ -433,11 +433,17 @@ func WrapCommandLinuxWithOptions(cfg *config.Config, command string, bridge *Lin // Skip Landlock wrapper if executable is in /tmp (test binaries are built there) // The wrapper won't work because --tmpfs /tmp hides the test binary executableInTmp := strings.HasPrefix(fenceExePath, "/tmp/") - useLandlockWrapper := opts.UseLandlock && features.CanUseLandlock() && fenceExePath != "" && !executableInTmp + // Skip Landlock wrapper if fence is being used as a library (executable is not fence) + // The wrapper re-executes the binary with --landlock-apply, which only fence understands + executableIsFence := strings.Contains(filepath.Base(fenceExePath), "fence") + useLandlockWrapper := opts.UseLandlock && features.CanUseLandlock() && fenceExePath != "" && !executableInTmp && executableIsFence if opts.Debug && executableInTmp { fmt.Fprintf(os.Stderr, "[fence:linux] Skipping Landlock wrapper (executable in /tmp, likely a test)\n") } + if opts.Debug && !executableIsFence { + fmt.Fprintf(os.Stderr, "[fence:linux] Skipping Landlock wrapper (running as library, not fence CLI)\n") + } bwrapArgs = append(bwrapArgs, "--", shellPath, "-c") diff --git a/internal/sandbox/linux_features_stub.go b/internal/sandbox/linux_features_stub.go index 3db8224..5544889 100644 --- a/internal/sandbox/linux_features_stub.go +++ b/internal/sandbox/linux_features_stub.go @@ -14,6 +14,7 @@ type LinuxFeatures struct { HasEBPF bool HasCapBPF bool HasCapRoot bool + CanUnshareNet bool KernelMajor int KernelMinor int } diff --git a/internal/sandbox/manager.go b/internal/sandbox/manager.go index c8fdc0f..1845f92 100644 --- a/internal/sandbox/manager.go +++ b/internal/sandbox/manager.go @@ -76,7 +76,9 @@ func (m *Manager) Initialize() error { m.linuxBridge = bridge // Set up reverse bridge for exposed ports (inbound connections) - if len(m.exposedPorts) > 0 { + // Only needed when network namespace is available - otherwise they share the network + features := DetectLinuxFeatures() + if len(m.exposedPorts) > 0 && features.CanUnshareNet { reverseBridge, err := NewReverseBridge(m.exposedPorts, m.debug) if err != nil { m.linuxBridge.Cleanup() @@ -85,6 +87,8 @@ func (m *Manager) Initialize() error { return fmt.Errorf("failed to initialize reverse bridge: %w", err) } m.reverseBridge = reverseBridge + } else if len(m.exposedPorts) > 0 && m.debug { + m.logDebug("Skipping reverse bridge (no network namespace, ports accessible directly)") } }