Refuse launch dirs overlapping sensitive dirs under every root
This commit is contained in:
@@ -339,6 +339,16 @@ func sensitiveDirs(home string) []string {
|
||||
return dirs
|
||||
}
|
||||
|
||||
// overlapsSensitive returns the sensitive dir that p is, sits under or contains.
|
||||
func overlapsSensitive(p, home string) string {
|
||||
for _, s := range sensitiveDirs(home) {
|
||||
if s = realpath(s); inside(p, s) || inside(s, p) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// allowedRoots reads the launch-dir roots; without the file the only root is
|
||||
// $HOME and def is true.
|
||||
func allowedRoots(home string) (roots []string, def bool, err error) {
|
||||
@@ -378,10 +388,8 @@ func allowedRoots(home string) (roots []string, def bool, err error) {
|
||||
return nil, false, fmt.Errorf("%s: %q is or contains $PATH entry %s", allowedPathsFile(), l, d)
|
||||
}
|
||||
}
|
||||
for _, s := range sensitiveDirs(home) {
|
||||
if s = realpath(s); inside(r, s) || inside(s, r) {
|
||||
return nil, false, fmt.Errorf("%s: %q overlaps %s", allowedPathsFile(), l, s)
|
||||
}
|
||||
if s := overlapsSensitive(r, home); s != "" {
|
||||
return nil, false, fmt.Errorf("%s: %q overlaps %s", allowedPathsFile(), l, s)
|
||||
}
|
||||
roots = append(roots, r)
|
||||
}
|
||||
@@ -399,7 +407,7 @@ func pathDirs() []string {
|
||||
|
||||
// checkLaunchDir returns pwd resolved if it is strictly under an allowed root,
|
||||
// never $HOME itself, anything holding the sandbox's config, the API socket or
|
||||
// a $PATH entry, nor anything overlapping a forbidden system dir. Under the
|
||||
// a $PATH entry, nor anything overlapping a forbidden system or sensitive dir. Under the
|
||||
// default $HOME root, dot-dirs of $HOME are refused too.
|
||||
func checkLaunchDir(pwd, allowlist string) (string, error) {
|
||||
pwd = realpath(pwd)
|
||||
@@ -426,6 +434,9 @@ func checkLaunchDir(pwd, allowlist string) (string, error) {
|
||||
return "", fmt.Errorf("refusing to mount %s: it overlaps %s", pwd, bad)
|
||||
}
|
||||
}
|
||||
if s := overlapsSensitive(pwd, home); s != "" {
|
||||
return "", fmt.Errorf("refusing to mount %s: it overlaps %s", pwd, s)
|
||||
}
|
||||
for _, r := range roots {
|
||||
if pwd != r && inside(pwd, r) {
|
||||
return pwd, nil
|
||||
|
||||
+31
-3
@@ -326,6 +326,18 @@ func TestLaunchRefusals(t *testing.T) {
|
||||
}},
|
||||
{"invalid-root", "home/proj", func(t *testing.T, e *env) { writePaths(t, e, "/run/x") }},
|
||||
{"var-tmp-under-var-root", "/var/tmp", func(t *testing.T, e *env) { writePaths(t, e, "/var") }},
|
||||
{"xdg-config-subdir", "home/cfg/systemd", func(t *testing.T, e *env) {
|
||||
_ = os.MkdirAll(e.work+"/home/cfg/systemd", 0o755)
|
||||
t.Setenv("XDG_CONFIG_HOME", e.work+"/home/cfg")
|
||||
}},
|
||||
{"xdg-data-home", "home/data", func(t *testing.T, e *env) {
|
||||
_ = os.MkdirAll(e.work+"/home/data", 0o755)
|
||||
t.Setenv("XDG_DATA_HOME", e.work+"/home/data")
|
||||
}},
|
||||
{"xdg-data-containers", "home/data/containers", func(t *testing.T, e *env) {
|
||||
_ = os.MkdirAll(e.work+"/home/data/containers", 0o755)
|
||||
t.Setenv("XDG_DATA_HOME", e.work+"/home/data")
|
||||
}},
|
||||
{"default-root-dot-dir", "home/.local/src/x", func(t *testing.T, e *env) {
|
||||
_ = os.MkdirAll(e.work+"/home/.local/src/x", 0o755)
|
||||
}},
|
||||
@@ -365,6 +377,9 @@ func TestCheckLaunchDirRuntimeFallback(t *testing.T) {
|
||||
forbiddenRoots = []string{"/run"}
|
||||
t.Cleanup(func() { forbiddenRoots = origRoots })
|
||||
_, err = checkLaunchDir("/tmp/home/proj", "/nonexistent/hosts")
|
||||
check(t, "under runtime dir", true, err != nil && strings.Contains(err.Error(), "overlaps "+realpath("/tmp")))
|
||||
t.Setenv("XDG_RUNTIME_DIR", "/nonexistent/rt")
|
||||
_, err = checkLaunchDir("/tmp/home/proj", "/nonexistent/hosts")
|
||||
check(t, "subdir", nil, err)
|
||||
}
|
||||
|
||||
@@ -579,10 +594,11 @@ func TestLaunchDotDirRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckLaunchDirRoots(t *testing.T) {
|
||||
cfg := t.TempDir()
|
||||
cfg, _ := filepath.EvalSymlinks(t.TempDir())
|
||||
t.Setenv("HOME", "/home/u")
|
||||
t.Setenv("XDG_CONFIG_HOME", cfg)
|
||||
t.Setenv("XDG_DATA_HOME", "")
|
||||
t.Setenv("XDG_DATA_HOME", "/srv/data")
|
||||
t.Setenv("XDG_RUNTIME_DIR", "/srv/rt")
|
||||
t.Setenv("PATH", "/usr/bin:/srv/tools/bin")
|
||||
f := filepath.Join(cfg, "agent-sandbox", "allowed-paths")
|
||||
_ = os.MkdirAll(filepath.Dir(f), 0o755)
|
||||
@@ -596,7 +612,7 @@ func TestCheckLaunchDirRoots(t *testing.T) {
|
||||
check(t, dir, ok, err == nil)
|
||||
}
|
||||
for _, bad := range []string{"/", "/home", "~", "~/", "~/.local", "~/.local/share", "~/.ssh", "~/.ssh/x",
|
||||
"~/.gnupg", "~/.config/foo", cfg, cfg + "/agent-sandbox/x", "~/.local/share/containers/s", "relative/x", "/run/user/1000", "/tmp/x", "/var/tmp", "/proc/1",
|
||||
"~/.gnupg", "~/.config/foo", "~/.local/share/containers/s", "relative/x", "/run/user/1000", "/tmp/x", "/var/tmp", "/proc/1",
|
||||
"/sys", "/dev/shm", "/usr", "/srv/tools", "/var"} {
|
||||
write("/srv/work\n" + bad)
|
||||
_, _, err := allowedRoots("/home/u")
|
||||
@@ -605,6 +621,18 @@ func TestCheckLaunchDirRoots(t *testing.T) {
|
||||
write("/var")
|
||||
_, err := checkLaunchDir("/var/tmp/x", "/nowhere/hosts")
|
||||
check(t, "/var/tmp under /var root", true, err != nil)
|
||||
|
||||
// cfg lives under /tmp, so drop that rule to leave only the sensitive one.
|
||||
origRoots := forbiddenRoots
|
||||
forbiddenRoots = []string{"/run"}
|
||||
t.Cleanup(func() { forbiddenRoots = origRoots })
|
||||
for bad, want := range map[string]string{cfg: cfg, cfg + "/agent-sandbox/x": cfg, "/srv/data/containers/x": "/srv/data/containers",
|
||||
"/srv/data": "/srv/data/containers", "~/.ssh/x": "/home/u/.ssh", "~/.gnupg/x": "/home/u/.gnupg",
|
||||
"~/.config/foo": "/home/u/.config", "~/.local/share/containers/s": "/home/u/.local/share/containers", "/srv/rt/x": "/srv/rt"} {
|
||||
write("/srv/work\n" + bad)
|
||||
_, _, err := allowedRoots("/home/u")
|
||||
check(t, "sensitive root "+bad, true, strings.Contains(fmt.Sprint(err), "overlaps "+want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchPassesArgsVerbatim(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user