77c72c0081c82ee9485e4ce795d8f2aae4292579

Author
Toby Padilla <toby@charm.sh>
Committer
Toby Padilla <toby@charm.sh>
Date

Message

Use authorized keys file to gate git write access

Diff

 1diff --git a/main.go b/main.go
 2index c36ff2a90ee21008d65c8f55d22e1b214b59d918..34c7c444bfef7f505bab53c414e76f5c83ffe084 100644
 3--- a/main.go
 4+++ b/main.go
 5@@ -13,9 +13,10 @@ import (
 6 )
 7 
 8 type Config struct {
 9-	Port     int    `env:"SMOOTHIE_PORT" default:"23231"`
10-	KeyPath  string `env:"SMOOTHIE_KEY_PATH" default:".ssh/smoothie_server_ed25519"`
11-	RepoPath string `env:"SMOOTHIE_REPO_PATH" default:".repos"`
12+	Port         int    `env:"SMOOTHIE_PORT" default:"23231"`
13+	KeyPath      string `env:"SMOOTHIE_KEY_PATH" default:".ssh/smoothie_server_ed25519"`
14+	RepoAuthPath string `env:"SMOOTHIE_KEY_PATH" default:".ssh/smoothie_git_authorized_keys"`
15+	RepoPath     string `env:"SMOOTHIE_REPO_PATH" default:".repos"`
16 }
17 
18 func main() {
19@@ -28,7 +29,7 @@ func main() {
20 		cfg.Port,
21 		cfg.KeyPath,
22 		bm.Middleware(tui.SessionHandler, tea.WithAltScreen()),
23-		gm.Middleware(cfg.RepoPath),
24+		gm.Middleware(cfg.RepoPath, cfg.RepoAuthPath),
25 		lm.Middleware(),
26 	)
27 	if err != nil {
28diff --git a/server/middleware/git/git.go b/server/middleware/git/git.go
29index 33ef2f0c32a2c626ce117b179ce91f86ae7173a8..d098a62bb6a1bd4d68da089fc2a3e2888f96b755 100644
30--- a/server/middleware/git/git.go
31+++ b/server/middleware/git/git.go
32@@ -1,8 +1,10 @@
33 package git
34 
35 import (
36+	"bufio"
37 	"context"
38 	"fmt"
39+	"log"
40 	"os"
41 	"os/exec"
42 	"smoothie/server/middleware"
43@@ -10,13 +12,50 @@ import (
44 	"github.com/gliderlabs/ssh"
45 )
46 
47-func Middleware(repoDir string) middleware.Middleware {
48+func Middleware(repoDir string, authorizedKeysPath string) middleware.Middleware {
49+	authedKeys := make([]ssh.PublicKey, 0)
50+	hasAuth, err := fileExists(authorizedKeysPath)
51+	if err != nil {
52+		log.Fatal(err)
53+	}
54+	if hasAuth {
55+		f, err := os.Open(authorizedKeysPath)
56+		if err != nil {
57+			log.Fatal(err)
58+		}
59+		defer f.Close()
60+		scanner := bufio.NewScanner(f)
61+		for scanner.Scan() {
62+			pt := scanner.Text()
63+			log.Printf("Adding authorized key: %s", pt)
64+			pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pt))
65+			if err != nil {
66+				log.Fatal(err)
67+			}
68+			authedKeys = append(authedKeys, pk)
69+		}
70+		if err := scanner.Err(); err != nil {
71+			log.Fatal(err)
72+		}
73+	}
74 	return func(sh ssh.Handler) ssh.Handler {
75 		return func(s ssh.Session) {
76 			cmd := s.Command()
77 			if len(cmd) == 2 {
78 				switch cmd[0] {
79-				case "git-upload-pack", "git-receive-pack", "git-upload-archive":
80+				case "git-upload-pack", "git-upload-archive", "git-receive-pack":
81+					if hasAuth && cmd[0] == "git-receive-pack" {
82+						authed := false
83+						for _, pk := range authedKeys {
84+							if ssh.KeysEqual(pk, s.PublicKey()) {
85+								authed = true
86+							}
87+						}
88+						if !authed {
89+							fatalGit(s, fmt.Errorf("you are not authorized to do this"))
90+							break
91+						}
92+					}
93 					r := cmd[1]
94 					rp := fmt.Sprintf("%s%s", repoDir, r)
95 					ctx := s.Context()