From b33f132643c9c24eeb122c16e5cd47a9fbbbcba5 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Wed, 29 Apr 2026 07:25:06 +0000 Subject: [PATCH] guard: add secure-mode sandboxing for SSH git operations --- guard/guard.go | 28 ++++++++++++++++++++++++++++ 1 file(s) changed, 28 insertion(s)(+), 0 deletion(s)(-) diff --git a/guard/guard.go b/guard/guard.go --- a/guard/guard.go +++ b/guard/guard.go @@ -11,9 +11,11 @@ "net/url" "os" "os/exec" "strings" + "syscall" securejoin "github.com/cyphar/filepath-securejoin" "github.com/urfave/cli/v3" + "tangled.org/core/knotserver/sandbox" "tangled.org/core/log" ) @@ -48,6 +50,10 @@ Name: "motd-file", Usage: "path to message of the day file", Value: "/home/git/motd", }, + &cli.BoolFlag{ + Name: "secure-mode", + Usage: "isolate git subprocesses to their own repository directory", + }, }, } } @@ -60,6 +66,7 @@ gitDir := cmd.String("git-dir") logPath := cmd.String("log-path") endpoint := cmd.String("internal-api") motdFile := cmd.String("motd-file") + secureMode := cmd.Bool("secure-mode") logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { @@ -157,6 +164,27 @@ gitCmd.Stdin = os.Stdin gitCmd.Env = append(os.Environ(), fmt.Sprintf("GIT_USER_DID=%s", incomingUser), ) + + if secureMode { + sb, warn := sandbox.New(func(repoPath string) (uint32, uint32, error) { + return sandbox.LookupUIDForRepoPath(gitDir, repoPath) + }) + if warn != "" { + l.Warn("secure-mode: sandbox degraded", "reason", warn) + } else { + l.Info("secure-mode: wrapping git command", "backend", sb.Name()) + } + wrapped, wrapErr := sb.Wrap(fullPath, gitCmd) + if wrapErr != nil { + l.Error("sandbox wrap failed", "error", wrapErr) + } else { + gitCmd = wrapped + } + } + + if gitCmd.SysProcAttr == nil { + gitCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + } if err := gitCmd.Run(); err != nil { l.Error("command failed", "error", err) -- tangled.sh