aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-06-20 02:42:35 +0900
committerOphestra <cat@gensokyo.uk>2026-06-20 02:42:35 +0900
commit58ce1347188b94743ea05a1492c53d53d0e4fa91 (patch)
tree6b5e93b7620096dc6ae1a96bcbaf2a58ca66fb50
parent2066093343c34a7b05d7fb0f1082ce513f2b4c0f (diff)
internal/outcome: attempt nscd path-hiding if present
This avoids creating the mount point on musl setups which accomplishes nothing and can run into permission problems. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/env/env.go28
-rw-r--r--internal/env/env_test.go5
-rw-r--r--internal/outcome/dispatcher_test.go2
-rw-r--r--internal/outcome/outcome.go2
-rw-r--r--internal/outcome/run_test.go2
-rw-r--r--internal/outcome/spcontainer.go23
6 files changed, 46 insertions, 16 deletions
diff --git a/internal/env/env.go b/internal/env/env.go
index 45fd5fcb..0c413733 100644
--- a/internal/env/env.go
+++ b/internal/env/env.go
@@ -1,21 +1,29 @@
-// Package env provides the [Paths] struct for efficiently building paths from the environment.
+// Package env provides the [Paths] struct for efficiently building paths from
+// the environment.
package env
import (
+ "errors"
+ "io/fs"
"log"
"os"
"strconv"
"hakurei.app/check"
+ "hakurei.app/fhs"
"hakurei.app/hst"
)
+const VarRunNscd = fhs.Var + "run/nscd"
+
// Paths holds paths copied from the environment and is used to create [hst.Paths].
type Paths struct {
// TempDir is returned by [os.TempDir].
TempDir *check.Absolute
// RuntimePath is copied from $XDG_RUNTIME_DIR.
RuntimePath *check.Absolute
+ // Whether [VarRunNscd] is a directory.
+ HasNscd bool
}
// Copy expands [Paths] into [hst.Paths].
@@ -37,14 +45,17 @@ func (env *Paths) Copy(v *hst.Paths, userid int) {
}
// CopyPaths returns a populated [Paths].
-func CopyPaths() *Paths { return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv) }
+func CopyPaths() *Paths {
+ return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv, os.Stat)
+}
-// CopyPathsFunc returns a populated [Paths],
-// using the provided [log.Fatalf], [os.TempDir], [os.Getenv] functions.
+// CopyPathsFunc returns a populated [Paths], using the provided [log.Fatalf],
+// [os.TempDir], [os.Getenv] functions.
func CopyPathsFunc(
fatalf func(format string, v ...any),
tempdir func() string,
getenv func(key string) string,
+ stat func(name string) (fs.FileInfo, error),
) *Paths {
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
@@ -61,5 +72,14 @@ func CopyPathsFunc(
env.RuntimePath = a
}
+ if fi, err := stat(VarRunNscd); err != nil {
+ if !errors.Is(err, fs.ErrNotExist) {
+ fatalf("%v", err)
+ panic("unreachable")
+ }
+ } else {
+ env.HasNscd = fi.IsDir()
+ }
+
return &env
}
diff --git a/internal/env/env_test.go b/internal/env/env_test.go
index 0e5db4a9..cce97da0 100644
--- a/internal/env/env_test.go
+++ b/internal/env/env_test.go
@@ -2,6 +2,7 @@ package env_test
import (
"fmt"
+ "io/fs"
"reflect"
"testing"
@@ -104,7 +105,9 @@ func TestCopyPaths(t *testing.T) {
t.Fatalf("fatalf: %q, want %q", got, tc.fatal)
}
panic(stub.PanicExit)
- }, func() string { return tc.tmp }, func(key string) string { return tc.env[key] })
+ }, func() string { return tc.tmp }, func(key string) string { return tc.env[key] }, func(name string) (fs.FileInfo, error) {
+ return nil, fs.ErrNotExist
+ })
if tc.fatal != "" {
t.Fatalf("copyPaths: expected fatal %q", tc.fatal)
diff --git a/internal/outcome/dispatcher_test.go b/internal/outcome/dispatcher_test.go
index 9ae2ba52..00771e56 100644
--- a/internal/outcome/dispatcher_test.go
+++ b/internal/outcome/dispatcher_test.go
@@ -23,6 +23,7 @@ import (
"hakurei.app/container/seccomp"
"hakurei.app/container/std"
"hakurei.app/hst"
+ "hakurei.app/internal/env"
"hakurei.app/internal/stub"
"hakurei.app/internal/system"
"hakurei.app/message"
@@ -174,6 +175,7 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
call("cmdOutput", stub.ExpectArgs{container.Nonexistent, os.Stderr, []string{}, "/"}, []byte("0"), nil),
call("tempdir", stub.ExpectArgs{}, container.Nonexistent+"/tmp", nil),
call("lookupEnv", stub.ExpectArgs{"XDG_RUNTIME_DIR"}, wantRuntimePath, nil),
+ call("stat", stub.ExpectArgs{env.VarRunNscd}, stubFileInfoIsDir(true), nil),
call("getuid", stub.ExpectArgs{}, 1000, nil),
call("getgid", stub.ExpectArgs{}, 100, nil),
diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go
index 4bdc2ede..0fe0016e 100644
--- a/internal/outcome/outcome.go
+++ b/internal/outcome/outcome.go
@@ -110,7 +110,7 @@ func newOutcomeState(k syscallDispatcher, msg message.Msg, id *hst.ID, config *h
Paths: env.CopyPathsFunc(k.fatalf, k.tempdir, func(key string) string {
v, _ := k.lookupEnv(key)
return v
- }),
+ }, k.stat),
Container: config.Container,
}
diff --git a/internal/outcome/run_test.go b/internal/outcome/run_test.go
index 2a412d8b..a1d83ec9 100644
--- a/internal/outcome/run_test.go
+++ b/internal/outcome/run_test.go
@@ -710,7 +710,7 @@ func (k *stubNixOS) lookupEnv(key string) (string, bool) {
func (k *stubNixOS) stat(name string) (fs.FileInfo, error) {
switch name {
case "/var/run/nscd":
- return nil, nil
+ return stubFileInfoIsDir(true), nil
case "/run/user/1971/pulse":
return nil, nil
case "/run/user/1971/pulse/native":
diff --git a/internal/outcome/spcontainer.go b/internal/outcome/spcontainer.go
index 97d98dd1..bfa5dbdc 100644
--- a/internal/outcome/spcontainer.go
+++ b/internal/outcome/spcontainer.go
@@ -18,13 +18,12 @@ import (
"hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/dbus"
+ "hakurei.app/internal/env"
"hakurei.app/internal/system"
"hakurei.app/internal/validate"
"hakurei.app/message"
)
-const varRunNscd = fhs.Var + "run/nscd"
-
func init() { gob.Register(new(spParamsOp)) }
// spParamsOp initialises unordered fields of [container.Params] and the
@@ -136,17 +135,23 @@ type spFilesystemOp struct {
}
func (s *spFilesystemOp) toSystem(state *outcomeStateSys) error {
- /* retrieve paths and hide them if they're made available in the sandbox;
-
- this feature tries to improve user experience of permissive defaults, and
- to warn about issues in custom configuration; it is NOT a security feature
- and should not be treated as such, ALWAYS be careful with what you bind */
+ // retrieve paths and hide them if they're made available in the sandbox
+ //
+ // this feature tries to improve user experience of permissive defaults, and
+ // to warn about issues in custom configuration; it is NOT a security feature
+ // and should not be treated as such, ALWAYS be careful with what you bind
hidePaths := []string{
state.sc.RuntimePath.String(),
state.sc.SharePath.String(),
+ }
- // this causes emulated passwd database to be bypassed on some /etc/ setups
- varRunNscd,
+ if state.Paths == nil || state.HasNscd {
+ hidePaths = append(hidePaths,
+ // this causes emulated passwd database to be bypassed on some /etc/
+ // setups, made optional to avoid needlessly creating it on
+ // non-glibc systems when invoking permissive defaults
+ env.VarRunNscd,
+ )
}
// dbus.Address does not go through syscallDispatcher