diff --git a/pkg/isolation/runtime.go b/pkg/isolation/runtime.go index 83c86b514..b2de98b88 100644 --- a/pkg/isolation/runtime.go +++ b/pkg/isolation/runtime.go @@ -218,7 +218,7 @@ func DefaultExposePaths(root string) []config.ExposePath { } func defaultLinuxSystemExposePaths() []config.ExposePath { - return []config.ExposePath{ + return existingExposePaths([]config.ExposePath{ {Source: "/usr", Target: "/usr", Mode: "ro"}, {Source: "/bin", Target: "/bin", Mode: "ro"}, {Source: "/lib", Target: "/lib", Mode: "ro"}, @@ -236,7 +236,19 @@ func defaultLinuxSystemExposePaths() []config.ExposePath { {Source: "/etc/alternatives", Target: "/etc/alternatives", Mode: "ro"}, {Source: "/usr/share/zoneinfo", Target: "/usr/share/zoneinfo", Mode: "ro"}, {Source: "/etc/localtime", Target: "/etc/localtime", Mode: "ro"}, + }) +} + +// existingExposePaths keeps only the builtin host paths that exist on the +// current machine so Linux isolation does not fail on distro-specific paths. +func existingExposePaths(items []config.ExposePath) []config.ExposePath { + filtered := make([]config.ExposePath, 0, len(items)) + for _, item := range items { + if _, err := os.Stat(item.Source); err == nil { + filtered = append(filtered, item) + } } + return filtered } // MergeExposePaths merges built-in rules with user overrides. Rules are keyed @@ -363,12 +375,12 @@ func Start(cmd *exec.Cmd) error { var err error root, err = ResolveInstanceRoot() if err != nil { - _ = cmd.Process.Kill() + terminateStartedCommand(cmd) return err } } if err := postStartPlatformIsolation(cmd, isolation, root); err != nil { - _ = cmd.Process.Kill() + terminateStartedCommand(cmd) return err } return nil @@ -390,17 +402,26 @@ func Run(cmd *exec.Cmd) error { var err error root, err = ResolveInstanceRoot() if err != nil { - _ = cmd.Process.Kill() + terminateStartedCommand(cmd) return err } } if err := postStartPlatformIsolation(cmd, isolation, root); err != nil { - _ = cmd.Process.Kill() + terminateStartedCommand(cmd) return err } return cmd.Wait() } +func terminateStartedCommand(cmd *exec.Cmd) { + cleanupPendingPlatformResources(cmd) + if cmd == nil || cmd.Process == nil { + return + } + _ = cmd.Process.Kill() + _ = cmd.Wait() +} + // PrepareCommand mutates the command in-place so it inherits the configured // isolated environment before being started by the caller. func PrepareCommand(cmd *exec.Cmd) error { diff --git a/pkg/isolation/runtime_test.go b/pkg/isolation/runtime_test.go index 0b6859319..213c4b065 100644 --- a/pkg/isolation/runtime_test.go +++ b/pkg/isolation/runtime_test.go @@ -176,12 +176,11 @@ func TestValidateWindowsExposePaths(t *testing.T) { func TestDefaultLinuxSystemExposePaths(t *testing.T) { paths := defaultLinuxSystemExposePaths() - needed := map[string]bool{ - "/etc/hosts": false, - "/etc/nsswitch.conf": false, - "/etc/ssl": false, - "/usr/share/zoneinfo": false, - "/etc/localtime": false, + needed := map[string]bool{} + for _, path := range []string{"/etc/hosts", "/etc/nsswitch.conf", "/etc/ssl", "/usr/share/zoneinfo", "/etc/localtime"} { + if _, err := os.Stat(path); err == nil { + needed[path] = false + } } for _, item := range paths { if _, ok := needed[item.Source]; ok { @@ -195,6 +194,23 @@ func TestDefaultLinuxSystemExposePaths(t *testing.T) { } } +func TestExistingExposePaths_SkipsMissingPaths(t *testing.T) { + existing := filepath.Join(t.TempDir(), "existing") + if err := os.MkdirAll(existing, 0o755); err != nil { + t.Fatalf("os.MkdirAll() error = %v", err) + } + filtered := existingExposePaths([]config.ExposePath{ + {Source: existing, Target: existing, Mode: "ro"}, + {Source: filepath.Join(t.TempDir(), "missing"), Target: "/missing", Mode: "ro"}, + }) + if len(filtered) != 1 { + t.Fatalf("existingExposePaths() len = %d, want 1", len(filtered)) + } + if got := filtered[0]; got.Source != existing { + t.Fatalf("existingExposePaths()[0] = %+v, want source=%q", got, existing) + } +} + func TestPrepareCommand_AppliesUserEnv(t *testing.T) { t.Setenv(config.EnvHome, filepath.Join(t.TempDir(), "home")) if runtime.GOOS == "linux" {