TL;DR
pledge("...", "stdio rpath wpath tty proc exec");
unveil("/dev/null", "rw");
// Differs by installation method?
// Better to make this configurable at build time.
unveil("/usr/local/bin/git", "rx");
// Depends on the git binary. This should be overridable too.
unveil("/usr/lib", "rx");
unveil("/usr/libexec", "rx");
unveil("/usr/local/lib", "rx");Motivation
I maintain a fork of git web frontend server (mirror) and it restricts filesystem access using unveil(2) on OpenBSD. I'd like to make it more secure by restricting syscalls using pledge(2).
Problem
That server's git
HTTP protocol implementation uses git executable
available on the system. Invocation is something like this:
cmd := exec.Command("git", []string{
"upload-pack",
"--stateless-rpc",
".",
})
cmd.Dir = repositoryPath
stdoutPipe, _ := cmd.StdoutPipe()
cmd.Stderr = cmd.StdoutI initially thought it just needs the usual
stdio rpath, but the child process will be killed without
any trace. Viewing ktrace, I realized git launches its
child process, so I added proc exec but OS still kills the
grantchild process. Passing every documented promises to
execpromises did not change the situation.
There is so little information on the internet about this topic.
After many hours of trial-and-error, I reached LLM (Kagi Assistant) as a
last resort. While it splits plausible looking but nonsensial text, it
successfully found a
Reddit thread questioning similar problem after patient prompts
(stating the link as the exact same symptom
.)
Appearently, OpenBSD kills a child process if access to dynamically linked paths were blocked by pledge / unveil.
This server has been running with unveil but without pledge. In that situation, OpenBSD seems to be lax on dynamically linked libraris on fork/exec (I'm not sure what's actually going on, this is speculation.) Because of this, I had been wrongly assuming "OpenBSD automagically unveils dynamically linked library paths."
Solution
ldd $(which git) then unveil displayed library paths in
rx mode. Once library paths were added, things went usual:
run it, see error, add pledge promise or unveil a path.
In addition to unveiling the library paths and pledging
proc exec promises, git-unload-pack needs:
- read and write access to
/dev/null(pledgerpath wpathand unveil the path) ttypledge (I have no idea, and would like to eliminate this)
Here is the actual code (and mirror in case Tangled server is down or returns HTTP 429.)
Moral of the story is, yeet your assumption when your solution is ineffective, probably.