repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

pico / pico
Antonio Mika · 08 Oct 24

ssh.go

  1package pico
  2
  3import (
  4	"context"
  5	"fmt"
  6	"os"
  7	"os/signal"
  8	"syscall"
  9	"time"
 10
 11	"github.com/charmbracelet/promwish"
 12	"github.com/charmbracelet/ssh"
 13	"github.com/charmbracelet/wish"
 14	bm "github.com/charmbracelet/wish/bubbletea"
 15	"github.com/muesli/termenv"
 16	"github.com/picosh/pico/db/postgres"
 17	"github.com/picosh/pico/shared"
 18	"github.com/picosh/pico/tui"
 19	wsh "github.com/picosh/pico/wish"
 20	"github.com/picosh/send/auth"
 21	"github.com/picosh/send/list"
 22	"github.com/picosh/send/pipe"
 23	wishrsync "github.com/picosh/send/protocols/rsync"
 24	"github.com/picosh/send/protocols/scp"
 25	"github.com/picosh/send/protocols/sftp"
 26	"github.com/picosh/send/proxy"
 27	"github.com/picosh/utils"
 28)
 29
 30func authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
 31	shared.SetPublicKey(ctx, key)
 32	return true
 33}
 34
 35func createRouter(cfg *shared.ConfigSite, handler *UploadHandler, cliHandler *CliHandler) proxy.Router {
 36	return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
 37		return []wish.Middleware{
 38			pipe.Middleware(handler, ""),
 39			list.Middleware(handler),
 40			scp.Middleware(handler),
 41			wishrsync.Middleware(handler),
 42			auth.Middleware(handler),
 43			wsh.PtyMdw(bm.MiddlewareWithColorProfile(tui.CmsMiddleware(cfg), termenv.ANSI256)),
 44			WishMiddleware(cliHandler),
 45			wsh.LogMiddleware(handler.GetLogger()),
 46		}
 47	}
 48}
 49
 50func withProxy(cfg *shared.ConfigSite, handler *UploadHandler, cliHandler *CliHandler, otherMiddleware ...wish.Middleware) ssh.Option {
 51	return func(server *ssh.Server) error {
 52		err := sftp.SSHOption(handler)(server)
 53		if err != nil {
 54			return err
 55		}
 56
 57		return proxy.WithProxy(createRouter(cfg, handler, cliHandler), otherMiddleware...)(server)
 58	}
 59}
 60
 61func StartSshServer() {
 62	host := utils.GetEnv("PICO_HOST", "0.0.0.0")
 63	port := utils.GetEnv("PICO_SSH_PORT", "2222")
 64	promPort := utils.GetEnv("PICO_PROM_PORT", "9222")
 65	cfg := NewConfigSite()
 66	logger := cfg.Logger
 67	dbpool := postgres.NewDB(cfg.DbURL, cfg.Logger)
 68	defer dbpool.Close()
 69
 70	handler := NewUploadHandler(
 71		dbpool,
 72		cfg,
 73	)
 74	cliHandler := &CliHandler{
 75		Logger: logger,
 76		DBPool: dbpool,
 77	}
 78
 79	s, err := wish.NewServer(
 80		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
 81		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
 82		wish.WithPublicKeyAuth(authHandler),
 83		withProxy(
 84			cfg,
 85			handler,
 86			cliHandler,
 87			promwish.Middleware(fmt.Sprintf("%s:%s", host, promPort), "pico-ssh"),
 88		),
 89	)
 90	if err != nil {
 91		logger.Error(err.Error())
 92		return
 93	}
 94
 95	done := make(chan os.Signal, 1)
 96	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
 97	logger.Info("starting SSH server on", "host", host, "port", port)
 98	go func() {
 99		if err = s.ListenAndServe(); err != nil {
100			logger.Error("serve", "err", err.Error())
101			os.Exit(1)
102		}
103	}()
104
105	<-done
106	logger.Info("stopping SSH server")
107	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
108	defer func() { cancel() }()
109	if err := s.Shutdown(ctx); err != nil {
110		logger.Error("shutdown", "err", err.Error())
111		os.Exit(1)
112	}
113}