summaryrefslogtreecommitdiff
path: root/internal/namespace/pinning.go
diff options
context:
space:
mode:
authorJames O'Doherty <james@theodohertyfamily.com>2026-06-03 23:45:45 -0400
committerJames O'Doherty <james@theodohertyfamily.com>2026-06-03 23:45:45 -0400
commit51a0845adba702ac02437405988b24b3b2c9fb45 (patch)
tree62174471b2bf2240f5cbe8532c991e33afce9e18 /internal/namespace/pinning.go
parentda70b10fbd056f19d892acad542ce96c40c58389 (diff)
fix: resolve resource leaks and improve namespace lifecycle management
- Fix DNS resolver leaks by creating temporary resolv.conf files within the profile's runtime directory and ensuring robust cleanup. - Fix isolation block directory leaks by explicitly removing the block directory during namespace unpinning. - Improve namespace lifecycle management: - Register processes before joining an active namespace to prevent race conditions in reference counting. - Update `IsLastProcess` and corresponding tests to reflect the unregister-then-check cleanup flow. - Improve test reliability and correctness: - Convert `TestAppRun_ProfileDirInjection` to use separate binary execution, preventing process replacement and ensuring `t.TempDir()` cleanup. - Replace hardcoded test configuration paths with `t.TempDir()` in `mount_leak_test.go`. - Implement `SetEnvOverrides` helper for cleaner environment variable management in E2E tests. - Improve E2E lifecycle tests with better environment handling and output redirection.
Diffstat (limited to 'internal/namespace/pinning.go')
-rw-r--r--internal/namespace/pinning.go29
1 files changed, 28 insertions, 1 deletions
diff --git a/internal/namespace/pinning.go b/internal/namespace/pinning.go
index 07f15f8..9bf4fee 100644
--- a/internal/namespace/pinning.go
+++ b/internal/namespace/pinning.go
@@ -12,6 +12,24 @@ import (
"golang.org/x/sys/unix"
)
+// blockPaths defines the host services that are bind-mounted over to block access
+// from within the isolated namespace.
+var blockPaths = []string{
+ "/run/dbus/system_bus_socket",
+ "/run/systemd/resolve/io.systemd.Resolve",
+ "/run/systemd/resolve/io.systemd.Resolve.Monitor",
+ "/run/nscd/socket",
+ "/var/run/dbus/system_bus_socket",
+ "/var/run/systemd/resolve/io.systemd.Resolve",
+ "/var/run/systemd/resolve/io.systemd.Resolve.Monitor",
+ "/var/run/nscd/socket",
+}
+
+// GetBlockPaths returns the list of paths blocked for namespace isolation.
+func GetBlockPaths() []string {
+ return blockPaths
+}
+
// PinNamespace binds the current network namespace to the profile's namespace path.
// This prevents the kernel from destroying the namespace when all processes exit.
func PinNamespace(pm *paths.PathManager, profile string) error {
@@ -44,7 +62,6 @@ func UnpinNamespace(pm *paths.PathManager, profile string) error {
}
// 1. Unmount the namespace first.
- // If this is the last reference to the namespace, the kernel will destroy it.
if err := unix.Unmount(nsPath, 0); err != nil {
return fmt.Errorf("failed to unmount namespace %s: %w", nsPath, err)
}
@@ -54,6 +71,16 @@ func UnpinNamespace(pm *paths.PathManager, profile string) error {
return fmt.Errorf("failed to remove pin file %s: %w", nsPath, err)
}
+ // 3. Unmount and clean up blocking services.
+ // Since the block files are located within the profile directory,
+ // we must unmount them before we can remove the directory.
+ for _, p := range GetBlockPaths() {
+ _ = unix.Unmount(p, unix.MNT_DETACH)
+ }
+
+ blockDir := filepath.Join(pm.RuntimeBaseDir(), "profiles", profile, "block")
+ _ = os.RemoveAll(blockDir)
+
pidsDir := GetPidsDirPath(pm, profile)
// Try to remove pids directory and empty parent directories