ssh.go

 1package server
 2
 3import (
 4	"context"
 5	"fmt"
 6	"log"
 7
 8	"github.com/charmbracelet/soft-serve/config"
 9	appCfg "github.com/charmbracelet/soft-serve/internal/config"
10	"github.com/charmbracelet/soft-serve/internal/tui"
11	"github.com/charmbracelet/wish"
12	bm "github.com/charmbracelet/wish/bubbletea"
13	gm "github.com/charmbracelet/wish/git"
14	lm "github.com/charmbracelet/wish/logging"
15	rm "github.com/charmbracelet/wish/recover"
16	"github.com/gliderlabs/ssh"
17)
18
19type SSHServer struct {
20	server *ssh.Server
21	cfg    *config.Config
22	ac     *appCfg.Config
23}
24
25// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
26// server key-pair will be created if none exists. An initial admin SSH public
27// key can be provided with authKey. If authKey is provided, access will be
28// restricted to that key. If authKey is not provided, the server will be
29// publicly writable until configured otherwise by cloning the `config` repo.
30func NewSSHServer(cfg *config.Config, ac *appCfg.Config) *SSHServer {
31	mw := []wish.Middleware{
32		rm.MiddlewareWithLogger(
33			cfg.ErrorLog,
34			softServeMiddleware(ac),
35			bm.Middleware(tui.SessionHandler(ac)),
36			gm.Middleware(cfg.RepoPath, ac),
37			lm.Middleware(),
38		),
39	}
40	s, err := wish.NewServer(
41		ssh.PublicKeyAuth(ac.PublicKeyHandler),
42		ssh.PasswordAuth(ac.PasswordHandler),
43		wish.WithAddress(fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.SSHPort)),
44		wish.WithHostKeyPath(cfg.KeyPath),
45		wish.WithMiddleware(mw...),
46	)
47	if err != nil {
48		log.Fatalln(err)
49	}
50	return &SSHServer{
51		server: s,
52		cfg:    cfg,
53		ac:     ac,
54	}
55}
56
57// Start starts the SSH server.
58func (s *SSHServer) Start() error {
59	return s.server.ListenAndServe()
60}
61
62// Shutdown lets the server gracefully shutdown.
63func (s *SSHServer) Shutdown(ctx context.Context) error {
64	return s.server.Shutdown(ctx)
65}