main ssh.go
Eric Bower  ·  2026-02-25
 1package patchbin
 2
 3import (
 4	"context"
 5	"fmt"
 6	"os"
 7	"path/filepath"
 8	"time"
 9
10	"github.com/picosh/pico/pkg/pssh"
11	"golang.org/x/crypto/ssh"
12)
13
14func authHandler(pr *PrCmd) func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
15	return func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
16		pubkey := pr.Backend.Pubkey(key)
17		userName := conn.User()
18		perms := &ssh.Permissions{
19			Extensions: map[string]string{
20				"pubkey": pubkey,
21			},
22		}
23		err := pr.IsBanned(pubkey, userName)
24		if err != nil {
25			pr.Backend.Logger.Info(
26				"user denied access",
27				"err", err,
28				"username", userName,
29				"pubkey", pubkey,
30			)
31			return perms, err
32		}
33		return perms, nil
34	}
35}
36
37func GitSshServer(ctx context.Context, cfg *GitCfg) *pssh.SSHServer {
38	dbpath := filepath.Join(cfg.DataDir, "pr.db?_fk=on")
39	dbh, err := SqliteOpen("file:"+dbpath, cfg.Logger)
40	if err != nil {
41		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
42	}
43
44	interval, err := time.ParseDuration(cfg.RateLimitInterval)
45	if err != nil {
46		panic(fmt.Sprintf("invalid rate_limit_interval: %s: %s", cfg.RateLimitInterval, err))
47	}
48
49	be := &Backend{
50		DB:      dbh,
51		Logger:  cfg.Logger,
52		Cfg:     cfg,
53		Limiter: NewRateLimiter(cfg.RateLimitCount, interval),
54	}
55
56	prCmd := &PrCmd{
57		Backend: be,
58	}
59
60	server, err := pssh.NewSSHServerWithConfig(
61		ctx,
62		cfg.Logger,
63		"patchbin",
64		cfg.Host,
65		cfg.SshPort,
66		cfg.PromPort,
67		filepath.Join(cfg.DataDir, "term_info_ed25519"),
68		authHandler(prCmd),
69		[]pssh.SSHServerMiddleware{
70			GitPatchRequestMiddleware(be, prCmd),
71		},
72		[]pssh.SSHServerMiddleware{},
73		nil,
74	)
75
76	if err != nil {
77		cfg.Logger.Error("failed to create ssh server", "err", err)
78		os.Exit(1)
79	}
80
81	return server
82}