From c1f95e2a03b5b3264ac3d029cc4efb4d0b152dd0 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 14 Feb 2019 15:03:46 -0500 Subject: Rewrite gitsrht-dispatch in Go --- gitsrht-dispatch | 67 -------------------------- gitsrht-dispatch/.gitignore | 1 + gitsrht-dispatch/go.mod | 3 ++ gitsrht-dispatch/go.sum | 2 + gitsrht-dispatch/main.go | 111 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 67 deletions(-) delete mode 100755 gitsrht-dispatch create mode 100644 gitsrht-dispatch/.gitignore create mode 100644 gitsrht-dispatch/go.mod create mode 100644 gitsrht-dispatch/go.sum create mode 100644 gitsrht-dispatch/main.go (limited to 'gitsrht-dispatch') diff --git a/gitsrht-dispatch b/gitsrht-dispatch deleted file mode 100755 index 39e8609..0000000 --- a/gitsrht-dispatch +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -# AuthorizedKeysCommand=/usr/bin/git-srht-dispatch "%u" "%h" "%t" "%k" -# AuthorizedKeysUser=root -import sys -import os -try: - f = open("/var/log/git-srht-dispatch", "a") - os.close(sys.stderr.fileno()) - os.dup2(f.fileno(), sys.stderr.fileno()) -except Exception as ex: - sys.stderr.write("Unable to open log for writing\n") - sys.stderr.write(str(ex) + "\n") -from collections import namedtuple -from datetime import datetime -from pwd import getpwnam -from grp import getgrnam -from srht.config import cfg, cfgkeys - -def log(s, *args): - sys.stderr.write("{} {}\n".format(datetime.now().isoformat(), - s.format(*args) if isinstance(s, str) else str(s))) - sys.stderr.flush() -log("Running git-srht-dispatch") - -def auth_keys_error(): - log("This command should be run by sshd's AuthorizedKeysCommand") - log('AuthorizedKeysCommand={} "%u" "%h" "%t" "%k"\nAuthorizedKeysUser=root', - os.path.abspath(sys.argv[0])) - sys.exit(1) - -Dispatcher = namedtuple("Dispatcher", ["cmd", "uid", "gid"]) -dispatchers = list() - -for cmd in cfgkeys("git.sr.ht::dispatch"): - user = cfg("git.sr.ht::dispatch", cmd).split(":") - uid, gid = getpwnam(user[0]).pw_uid, getgrnam(user[-1]).gr_gid - dispatchers.append(Dispatcher(cmd=cmd, uid=uid, gid=gid)) - log("registered dispatcher for {}:{}: {}", uid, gid, cmd) - -if len(sys.argv) != 5: - auth_keys_error() - -user = sys.argv[1] -uid = getpwnam(user).pw_uid -homedir = sys.argv[2] -key_type = sys.argv[3] -b64key = sys.argv[4] -authorized_keys_file = "{}/.ssh/authorized_keys".format(homedir) - -log("authorizing user={} ({}) home={} b64key={} key_type={}", - user, uid, homedir, b64key, key_type) - -for dispatch in dispatchers: - if dispatch.uid == uid: - log("dispatching to {} with uid={}, gid={}", - dispatch.cmd, dispatch.uid, dispatch.gid) - os.setgid(dispatch.gid) - os.setuid(dispatch.uid) - os.execl(dispatch.cmd, *([dispatch.cmd] + sys.argv[1:])) - -log("Falling back to existing authorized keys file") -if not os.path.exists(authorized_keys_file): - sys.exit(0) -with open(authorized_keys_file, "r") as f: - authorized_keys = f.read() -print(authorized_keys) -sys.exit(0) diff --git a/gitsrht-dispatch/.gitignore b/gitsrht-dispatch/.gitignore new file mode 100644 index 0000000..4644fc2 --- /dev/null +++ b/gitsrht-dispatch/.gitignore @@ -0,0 +1 @@ +gitsrht-dispatch diff --git a/gitsrht-dispatch/go.mod b/gitsrht-dispatch/go.mod new file mode 100644 index 0000000..bc3195a --- /dev/null +++ b/gitsrht-dispatch/go.mod @@ -0,0 +1,3 @@ +module git.sr.ht/~sircmpwn/git.sr.ht/gitsrht-dispatch + +require github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec diff --git a/gitsrht-dispatch/go.sum b/gitsrht-dispatch/go.sum new file mode 100644 index 0000000..bac961c --- /dev/null +++ b/gitsrht-dispatch/go.sum @@ -0,0 +1,2 @@ +github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks= +github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw= diff --git a/gitsrht-dispatch/main.go b/gitsrht-dispatch/main.go new file mode 100644 index 0000000..d7aee14 --- /dev/null +++ b/gitsrht-dispatch/main.go @@ -0,0 +1,111 @@ +package main + +import ( + "fmt" + "log" + "io" + "os" + osuser "os/user" + "strconv" + "strings" + "syscall" + + "github.com/vaughan0/go-ini" +) + +type Dispatcher struct { + cmd string + uid int + gid int +} + +func main() { + var ( + config ini.File + err error + logger *log.Logger + ) + + logf, err := os.OpenFile("/var/log/gitsrht-dispatch", + os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + log.Printf("Warning: unable to open log file: %v " + + "(using stderr instead)", err) + logger = log.New(os.Stderr, "", log.LstdFlags) + } else { + logger = log.New(logf, "", log.LstdFlags) + } + + logger.Println("Running git.sr.ht dispatch") + + for _, path := range []string{"../config.ini", "/etc/sr.ht/config.ini"} { + config, err = ini.LoadFile(path) + if err == nil { + break + } + } + if err != nil { + logger.Fatalf("Failed to load config file: %v", err) + } + + if len(os.Args) != 5 { + logger.Fatalf(`Error: This command should be run by sshd's AuthorizedKeysCommand: + +AuthorizedKeysCommand=%s "%%u" "%%h" "%%t" "%%k" +AuthorizedKeysUser=root`, os.Args[0]) + } + + // Map uid -> dispatcher + dispatchers := make(map[int]Dispatcher) + for cmd, value := range config.Section("git.sr.ht::dispatch") { + spec := strings.Split(value, ":") + if len(spec) != 2 { + logger.Fatalf("Expected %s=user:group", cmd) + } + user, err := osuser.Lookup(spec[0]) + if err != nil { + logger.Fatalf("Error looking up user %s: %v", spec[0], err) + } + group, err := osuser.LookupGroup(spec[1]) + if err != nil { + logger.Fatalf("Error looking up group %s: %v", spec[1], err) + } + uid, _ := strconv.Atoi(user.Uid) + gid, _ := strconv.Atoi(group.Gid) + dispatchers[uid] = Dispatcher{cmd, uid, gid} + logger.Printf("Registered dispatcher for %s(%d):%s(%d): %s", + spec[0], uid, spec[1], gid, cmd) + } + + var user *osuser.User + username := os.Args[1] + if user, err = osuser.Lookup(username); err != nil { + logger.Fatalf("Unknown user %s", username) + } + homedir := os.Args[2] + key_type := os.Args[3] + b64key := os.Args[4] + authorized_keys_file := fmt.Sprintf("%s/.ssh/authorized_keys", homedir) + uid, _ := strconv.Atoi(user.Uid) + + logger.Printf("Authorizing user %s (%d); home=%s; b64key=%s; key_type=%s", + username, uid, homedir, b64key, key_type) + + if dispatcher, ok := dispatchers[uid]; ok { + logger.Printf("Dispatching to %s", dispatcher.cmd) + syscall.Setgid(dispatcher.gid) + syscall.Setuid(dispatcher.uid) + if err := syscall.Exec(dispatcher.cmd, append([]string{ + dispatcher.cmd, + }, os.Args[1:]...), os.Environ()); err != nil { + logger.Fatalf("Error exec'ing into %s: %v", dispatcher.cmd, err) + } + } + + logger.Println("Falling back to authorized_keys file") + akf, err := os.Open(authorized_keys_file) + if err != nil { + logger.Fatalf("Error opening authorized_keys: %v", err) + } + io.Copy(os.Stdout, akf) +} -- cgit v1.2.3