Exec git under pledge and unveil on OpenBSD

Minimum pledge promises and unveil paths required to start git-upload-pack as a child process on OpenBSD.

Created at
Updated at

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.Stdout

I 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:

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.