this repo has no description
1package main 2 3import ( 4 "flag" 5 "fmt" 6 "log" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "time" 12) 13 14var ( 15 logger *log.Logger 16 logFile *os.File 17 clientIP string 18 19 // Command line flags 20 allowedUser = flag.String("user", "", "Allowed git user") 21 baseDirFlag = flag.String("base-dir", "/home/git", "Base directory for git repositories") 22 logPathFlag = flag.String("log-path", "/var/log/git-wrapper.log", "Path to log file") 23) 24 25func main() { 26 flag.Parse() 27 28 defer cleanup() 29 initLogger() 30 31 // Get client IP from SSH environment 32 if connInfo := os.Getenv("SSH_CONNECTION"); connInfo != "" { 33 parts := strings.Fields(connInfo) 34 if len(parts) > 0 { 35 clientIP = parts[0] 36 } 37 } 38 39 if *allowedUser == "" { 40 exitWithLog("access denied: no user specified") 41 } 42 43 sshCommand := os.Getenv("SSH_ORIGINAL_COMMAND") 44 45 logEvent("Connection attempt", map[string]interface{}{ 46 "user": *allowedUser, 47 "command": sshCommand, 48 "client": clientIP, 49 }) 50 51 if sshCommand == "" { 52 exitWithLog("access denied: we don't serve interactive shells :)") 53 } 54 55 cmdParts := strings.Fields(sshCommand) 56 if len(cmdParts) < 2 { 57 exitWithLog("invalid command format") 58 } 59 60 gitCommand := cmdParts[0] 61 repoName := strings.Trim(cmdParts[1], "'") 62 63 validCommands := map[string]bool{ 64 "git-receive-pack": true, 65 "git-upload-pack": true, 66 "git-upload-archive": true, 67 } 68 if !validCommands[gitCommand] { 69 exitWithLog("access denied: invalid git command") 70 } 71 72 if !isAllowedUser(*allowedUser, repoName) { 73 exitWithLog("access denied: user not allowed") 74 } 75 76 fullPath := filepath.Join(*baseDirFlag, repoName) 77 fullPath = filepath.Clean(fullPath) 78 79 logEvent("Processing command", map[string]interface{}{ 80 "user": *allowedUser, 81 "command": gitCommand, 82 "repo": repoName, 83 "fullPath": fullPath, 84 "client": clientIP, 85 }) 86 87 cmd := exec.Command(gitCommand, fullPath) 88 cmd.Stdout = os.Stdout 89 cmd.Stderr = os.Stderr 90 cmd.Stdin = os.Stdin 91 92 if err := cmd.Run(); err != nil { 93 exitWithLog(fmt.Sprintf("command failed: %v", err)) 94 } 95 96 logEvent("Command completed", map[string]interface{}{ 97 "user": *allowedUser, 98 "command": gitCommand, 99 "repo": repoName, 100 "success": true, 101 }) 102} 103 104func initLogger() { 105 var err error 106 logFile, err = os.OpenFile(*logPathFlag, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) 107 if err != nil { 108 fmt.Fprintf(os.Stderr, "failed to open log file: %v\n", err) 109 os.Exit(1) 110 } 111 112 logger = log.New(logFile, "", 0) 113} 114 115func logEvent(event string, fields map[string]interface{}) { 116 entry := fmt.Sprintf( 117 "timestamp=%q event=%q", 118 time.Now().Format(time.RFC3339), 119 event, 120 ) 121 122 for k, v := range fields { 123 entry += fmt.Sprintf(" %s=%q", k, v) 124 } 125 126 logger.Println(entry) 127} 128 129func exitWithLog(message string) { 130 logEvent("Access denied", map[string]interface{}{ 131 "error": message, 132 }) 133 logFile.Sync() 134 fmt.Fprintf(os.Stderr, "error: %s\n", message) 135 os.Exit(1) 136} 137 138func cleanup() { 139 if logFile != nil { 140 logFile.Sync() 141 logFile.Close() 142 } 143} 144 145func isAllowedUser(user, repoPath string) bool { 146 fullPath := filepath.Join(*baseDirFlag, repoPath) 147 didPath := filepath.Join(fullPath, "did") 148 149 didBytes, err := os.ReadFile(didPath) 150 if err != nil { 151 return false 152 } 153 154 allowedUser := strings.TrimSpace(string(didBytes)) 155 return allowedUser == user 156}