repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

Eric Bower  ·  2024-11-12

ssh.go

 1package git
 2
 3import (
 4	"context"
 5	"fmt"
 6	"os"
 7	"os/signal"
 8	"path/filepath"
 9	"syscall"
10	"time"
11
12	"github.com/charmbracelet/ssh"
13	"github.com/charmbracelet/wish"
14)
15
16func authHandler(pr *PrCmd) func(ctx ssh.Context, key ssh.PublicKey) bool {
17	return func(ctx ssh.Context, key ssh.PublicKey) bool {
18		pubkey := pr.Backend.Pubkey(key)
19		userName := ctx.User()
20		err := pr.IsBanned(pubkey, userName)
21		if err != nil {
22			pr.Backend.Logger.Info(
23				"user denied access",
24				"err", err,
25				"username", userName,
26				"pubkey", pubkey,
27			)
28			return false
29		}
30		return true
31	}
32}
33
34func GitSshServer(cfg *GitCfg, killCh chan error) {
35	dbpath := filepath.Join(cfg.DataDir, "pr.db?_fk=on")
36	dbh, err := SqliteOpen("file:"+dbpath, cfg.Logger)
37	if err != nil {
38		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
39	}
40
41	be := &Backend{
42		DB:     dbh,
43		Logger: cfg.Logger,
44		Cfg:    cfg,
45	}
46	prCmd := &PrCmd{
47		Backend: be,
48	}
49
50	s, err := wish.NewServer(
51		wish.WithAddress(
52			fmt.Sprintf("%s:%s", cfg.Host, cfg.SshPort),
53		),
54		wish.WithHostKeyPath(
55			filepath.Join(cfg.DataDir, "term_info_ed25519"),
56		),
57		wish.WithPublicKeyAuth(authHandler(prCmd)),
58		wish.WithMiddleware(
59			GitPatchRequestMiddleware(be, prCmd),
60		),
61	)
62
63	if err != nil {
64		cfg.Logger.Error("could not create server", "err", err)
65		return
66	}
67
68	done := make(chan os.Signal, 1)
69	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
70	cfg.Logger.Info("starting SSH server", "host", cfg.Host, "port", cfg.SshPort)
71	go func() {
72		if err = s.ListenAndServe(); err != nil {
73			cfg.Logger.Error("serve error", "err", err)
74			// os.Exit(1)
75		}
76	}()
77
78	select {
79	case <-done:
80	case <-killCh:
81	}
82	cfg.Logger.Info("stopping SSH server")
83	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
84	defer func() { cancel() }()
85	if err := s.Shutdown(ctx); err != nil {
86		cfg.Logger.Error("shutdown", "err", err)
87		// os.Exit(1)
88	}
89}