t3code: harden dispatch — dedicated user + validated t3-mint + scoped sudoers

Run t3-dispatch as an unprivileged dedicated user instead of wizard (who has
full sudo). Privileged minting goes through /usr/local/bin/t3-mint, which
validates the target against /etc/ttyd-user-map before minting as that user;
sudoers permits t3-dispatch to run only that wrapper. Compromise of the
network-facing service can mint pairing tokens for mapped users at most.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-01 22:40:53 +00:00
parent 0472f67d49
commit 9f551e3c13
4 changed files with 26 additions and 4 deletions

View file

@ -62,9 +62,10 @@ func lookup(ak string) (entry, bool) {
// user, via the scoped sudoers entry) and exchanges it at the instance's
// /api/auth/bootstrap, relaying the returned t3_session Set-Cookie to the browser.
func autoPair(e entry, w http.ResponseWriter, r *http.Request) {
out, err := exec.Command("sudo", "-n", "-u", e.OsUser,
"t3", "auth", "pairing", "create",
"--base-dir", "/home/"+e.OsUser+"/.t3", "--ttl", "5m", "--json").Output()
// t3-mint (root, via scoped sudoers) validates the OS user is in
// /etc/ttyd-user-map, then mints as that user. The dispatch service itself
// runs unprivileged and can invoke nothing else.
out, err := exec.Command("sudo", "-n", "/usr/local/bin/t3-mint", e.OsUser).Output()
if err != nil {
log.Printf("mint for %s failed: %v", e.OsUser, err)
http.Error(w, "pairing mint failed", http.StatusInternalServerError)