~edwargix/git.sr.ht

c1f95e2a03b5b3264ac3d029cc4efb4d0b152dd0 — Drew DeVault 7 years ago 8c828ab
Rewrite gitsrht-dispatch in Go
7 files changed, 118 insertions(+), 69 deletions(-)

D gitsrht-dispatch
A gitsrht-dispatch/.gitignore
A gitsrht-dispatch/go.mod
A gitsrht-dispatch/go.sum
A gitsrht-dispatch/main.go
M gitsrht/service.py
M setup.py
D gitsrht-dispatch => gitsrht-dispatch +0 -67
@@ 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)

A gitsrht-dispatch/.gitignore => gitsrht-dispatch/.gitignore +1 -0
@@ 0,0 1,1 @@
gitsrht-dispatch

A gitsrht-dispatch/go.mod => gitsrht-dispatch/go.mod +3 -0
@@ 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

A gitsrht-dispatch/go.sum => gitsrht-dispatch/go.sum +2 -0
@@ 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=

A gitsrht-dispatch/main.go => gitsrht-dispatch/main.go +111 -0
@@ 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)
}

M gitsrht/service.py => gitsrht/service.py +1 -1
@@ 46,7 46,7 @@ class GitOAuthService(AbstractOAuthService):
        webhooks.update({
            webhook_url: ["ssh-key:add", "ssh-key:remove"]
        })
        super().ensure_meta_webhooks(user, webhooks)
        return super().ensure_meta_webhooks(user, webhooks)

    def lookup_or_register(self, token, token_expires, scopes):
        user = super().lookup_or_register(token, token_expires, scopes)

M setup.py => setup.py +0 -1
@@ 60,7 60,6 @@ setup(
      ]
  },
  scripts = [
      'gitsrht-dispatch',
      'gitsrht-keys',
      'gitsrht-migrate',
      'gitsrht-periodic',