From caad1db81c40e7b0cf1c2d9b12ad9d3b088f18d8 Mon Sep 17 00:00:00 2001 From: Denis Laxalde Date: Sat, 14 Dec 2019 21:04:25 +0100 Subject: [PATCH] Make gitsrht-keys use srht-keys module from scm.sr.ht Only the main() is kept and it uses srhtkeys's functions. --- gitsrht-keys/go.mod | 3 +- gitsrht-keys/go.sum | 1 + gitsrht-keys/main.go | 184 +++++-------------------------------------- 3 files changed, 23 insertions(+), 165 deletions(-) diff --git a/gitsrht-keys/go.mod b/gitsrht-keys/go.mod index f50a7ba..de5964a 100644 --- a/gitsrht-keys/go.mod +++ b/gitsrht-keys/go.mod @@ -3,8 +3,7 @@ module git.sr.ht/~sircmpwn/git.sr.ht/gitsrht-keys go 1.13 require ( + git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys v0.0.0 github.com/go-redis/redis v6.15.6+incompatible - github.com/google/uuid v1.1.1 - github.com/lib/pq v1.2.0 github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec ) diff --git a/gitsrht-keys/go.sum b/gitsrht-keys/go.sum index 0c85869..b5be4b1 100644 --- a/gitsrht-keys/go.sum +++ b/gitsrht-keys/go.sum @@ -1,3 +1,4 @@ +git.sr.ht/~sircmpwn/scm.sr.ht v0.0.0-20191204153510-f740494d6b60 h1:24gw/gL9vvY2v7HDGwLOK9dsq2QOCbDTDzsVoyS0jeo= github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg= github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= diff --git a/gitsrht-keys/main.go b/gitsrht-keys/main.go index 93a3419..05bf3cf 100644 --- a/gitsrht-keys/main.go +++ b/gitsrht-keys/main.go @@ -1,150 +1,33 @@ package main import ( - "database/sql" - "encoding/json" - "fmt" - "io/ioutil" "log" - "net/http" "os" "path" goredis "github.com/go-redis/redis" - "github.com/google/uuid" - _ "github.com/lib/pq" "github.com/vaughan0/go-ini" + "git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys" ) -type KeyCache struct { - UserId int `json:"user_id"` - Username string `json:"username"` -} - -// We don't need everything, so we don't include everything. -type MetaUser struct { - Username string `json:"name"` -} - -// We don't need everything, so we don't include everything. -type MetaSSHKey struct { - Id int `json:"id"` - Fingerprint string `json:"fingerprint"` - Key string `json:"key"` - Owner MetaUser `json:"owner"` -} - -// Stores the SSH key in the database and returns the user's ID. -func storeKey(logger *log.Logger, db *sql.DB, key *MetaSSHKey) int { - logger.Println("Storing meta.sr.ht key in git.sr.ht database") - - // Getting the user ID is really a separate concern, but this saves us a - // SQL roundtrip and this is a performance-critical section - query, err := db.Prepare(` - WITH key_owner AS ( - SELECT id user_id - FROM "user" - WHERE "user".username = $1 - ) - INSERT INTO sshkey ( - user_id, - meta_id, - key, - fingerprint - ) - SELECT user_id, $2, $3, $4 - FROM key_owner - -- This no-ops on conflict, but we still need this query to complete so - -- that we can extract the user ID. DO NOTHING returns zero rows. - ON CONFLICT (meta_id) DO UPDATE SET meta_id = $2 - RETURNING id, user_id; - `) - if err != nil { - logger.Printf("Failed to prepare key insertion statement: %v", err) - return 0 - } - defer query.Close() - - var ( - userId int - keyId int - ) - if err = query.QueryRow(key.Owner.Username, - key.Id, key.Key, key.Fingerprint).Scan(&keyId, &userId); err != nil { - - logger.Printf("Error inserting key: %v", err) - } - - logger.Printf("Stored key %d for user %d", keyId, userId) - return userId -} - -func fetchKeysFromMeta(logger *log.Logger, config ini.File, - redis *goredis.Client, b64key string) (string, int) { - - meta, ok := config.Get("meta.sr.ht", "internal-origin") - if !ok { - meta, ok = config.Get("meta.sr.ht", "origin") - } - if !ok && meta == "" { - logger.Fatalf("No origin configured for meta.sr.ht") - } - - resp, err := http.Get(fmt.Sprintf("%s/api/ssh-key/%s", meta, b64key)) - if err != nil { - logger.Printf("meta.sr.ht http.Get: %v", err) - return "", 0 - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - logger.Printf("non-200 response from meta.sr.ht: %d", resp.StatusCode) - return "", 0 - } - - body, err := ioutil.ReadAll(resp.Body) - var key MetaSSHKey - if err = json.Unmarshal(body, &key); err != nil { - return "", 0 - } - - // We wait to connect to postgres until we know we must - pgcs, ok := config.Get("git.sr.ht", "connection-string") - if !ok { - logger.Fatalf("No connection string configured for git.sr.ht: %v", err) - } - db, err := sql.Open("postgres", pgcs) - if err != nil { - logger.Fatalf("Failed to open a database connection: %v", err) - } - userId := storeKey(logger, db, &key) - logger.Println("Fetched key from meta.sr.ht") - - // Cache in Redis too - cacheKey := fmt.Sprintf("git.sr.ht.ssh-keys.%s", b64key) - cache := KeyCache{ - UserId: userId, - Username: key.Owner.Username, - } - cacheBytes, err := json.Marshal(&cache) - if err != nil { - logger.Printf("Caching SSH key in redis failed: %v", err) - } else { - redis.Set(cacheKey, cacheBytes, 0) - } - - return key.Owner.Username, userId -} - func main() { // gitsrht-keys is run by sshd to generate an authorized_key file on stdout. // In order to facilitate this, we do one of two things: // - Attempt to fetch the cached key info from Redis (preferred) // - Fetch the key from meta.sr.ht and store it in SQL and Redis (slower) + service := "git.sr.ht" + shellName := "gitsrht-shell" + logFile := "/var/log/gitsrht-keys" var ( - config ini.File - err error - logger *log.Logger + config ini.File + err error + logger *log.Logger + username string + userId int + b64key string + keyType string + prefix string ) // TODO: update key last used timestamp on meta.sr.ht @@ -154,7 +37,7 @@ func main() { } redis := goredis.NewClient(&goredis.Options{Addr: redisHost}) - logf, err := os.OpenFile("/var/log/gitsrht-keys", + logf, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { log.Printf("Warning: unable to open log file: %v "+ @@ -174,49 +57,24 @@ func main() { logger.Fatalf("Failed to load config file: %v", err) } - if len(os.Args) < 5 { - logger.Fatalf("Expected four arguments from SSH") - } - logger.Printf("os.Args: %v", os.Args) - keyType := os.Args[3] - b64key := os.Args[4] - - var ( - username string - userId int - ) - cacheKey := fmt.Sprintf("git.sr.ht.ssh-keys.%s", b64key) - logger.Printf("Cache key for SSH key lookup: %s", cacheKey) - cacheBytes, err := redis.Get(cacheKey).Bytes() + keyType, b64key, prefix, err = srhtkeys.ParseArgs(logger) if err != nil { - logger.Println("Cache miss, going to meta.sr.ht") - username, userId = fetchKeysFromMeta(logger, config, redis, b64key) - } else { - var cache KeyCache - if err = json.Unmarshal(cacheBytes, &cache); err != nil { - logger.Fatalf("Unmarshal cache JSON: %v", err) - } - userId = cache.UserId - username = cache.Username - logger.Printf("Cache hit: %d %s", userId, username) + os.Exit(0) } + username, userId = srhtkeys.UserFromKey(logger, config, redis, service, b64key) + if username == "" { logger.Println("Unknown public key") os.Exit(0) } - defaultShell := path.Join(path.Dir(os.Args[0]), "gitsrht-shell") - shell, ok := config.Get("git.sr.ht", "shell") + defaultShell := path.Join(prefix, shellName) + shell, ok := config.Get(service, "shell") if !ok { shell = defaultShell } - push := uuid.New() - logger.Printf("Assigned uuid %s to this push", push.String()) - shellCommand := fmt.Sprintf("%s '%d' '%s' '%s'", - shell, userId, username, b64key) - fmt.Printf(`restrict,command="%s",`+ - `environment="SRHT_PUSH=%s" %s %s %s`+"\n", - shellCommand, push.String(), keyType, b64key, username) + srhtkeys.RenderAuthorizedKeysEntry(logger, shell, userId, username, + b64key, keyType) } -- 2.38.4