@@ -203,10 +203,23 @@ var sysExec = syscall.Exec |
| 203 | 203 | // authorizedKeysLine builds the single line sshd consumes. The forced |
| 204 | 204 | // command runs `shithubd ssh-shell <user_id>`; the option set strips |
| 205 | 205 | // every interactive affordance. |
| 206 | +// |
| 207 | +// The command uses the BARE binary name (`shithubd`), not the absolute |
| 208 | +// path of os.Args[0]. Reason: when the git user's login shell is |
| 209 | +// /usr/bin/git-shell (kept as a defense-in-depth wall — git-shell |
| 210 | +// only allows git's own commands plus entries in ~/git-shell-commands/), |
| 211 | +// git-shell rejects any first token that contains a slash. With a |
| 212 | +// bare token, git-shell looks for ~git/git-shell-commands/shithubd |
| 213 | +// (must be a symlink to /usr/local/bin/shithubd installed by ansible) |
| 214 | +// and execs it. |
| 215 | +// |
| 216 | +// PATH on the AKC's child process inherits sshd's env, which on |
| 217 | +// Debian includes /usr/local/bin — so a fallback `shithubd` lookup |
| 218 | +// would also find the binary even without git-shell-commands. The |
| 219 | +// git-shell-commands symlink is the authoritative path. |
| 206 | 220 | func authorizedKeysLine(row usersdb.UserSshKey) string { |
| 207 | | - binary := os.Args[0] |
| 208 | | - // Quote-escape only the binary path; user_id is a digit string so it |
| 209 | | - // can never contain shell metacharacters. |
| 221 | + binary := filepath.Base(os.Args[0]) |
| 222 | + // user_id is a digit string so it can never contain shell metacharacters. |
| 210 | 223 | cmd := fmt.Sprintf(`%s ssh-shell %d`, binary, row.UserID) |
| 211 | 224 | options := strings.Join([]string{ |
| 212 | 225 | fmt.Sprintf(`command="%s"`, cmd), |