repos / pico

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

pico / feeds
Eric Bower · 17 Jun 24

ssh.go

  1package feeds
  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	"github.com/picosh/pico/db/postgres"
 15	"github.com/picosh/pico/filehandlers"
 16	"github.com/picosh/pico/filehandlers/util"
 17	"github.com/picosh/pico/shared"
 18	"github.com/picosh/pico/shared/storage"
 19	wsh "github.com/picosh/pico/wish"
 20	"github.com/picosh/send/list"
 21	"github.com/picosh/send/pipe"
 22	"github.com/picosh/send/proxy"
 23	"github.com/picosh/send/send/auth"
 24	wishrsync "github.com/picosh/send/send/rsync"
 25	"github.com/picosh/send/send/scp"
 26	"github.com/picosh/send/send/sftp"
 27)
 28
 29func createRouter(handler *filehandlers.FileHandlerRouter) proxy.Router {
 30	return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
 31		return []wish.Middleware{
 32			pipe.Middleware(handler, ".txt"),
 33			list.Middleware(handler),
 34			scp.Middleware(handler),
 35			wishrsync.Middleware(handler),
 36			auth.Middleware(handler),
 37			wsh.PtyMdw(wsh.DeprecatedNotice()),
 38			wsh.LogMiddleware(handler.GetLogger()),
 39		}
 40	}
 41}
 42
 43func withProxy(handler *filehandlers.FileHandlerRouter, otherMiddleware ...wish.Middleware) ssh.Option {
 44	return func(server *ssh.Server) error {
 45		err := sftp.SSHOption(handler)(server)
 46		if err != nil {
 47			return err
 48		}
 49
 50		return proxy.WithProxy(createRouter(handler), otherMiddleware...)(server)
 51	}
 52}
 53
 54func StartSshServer() {
 55	host := shared.GetEnv("LISTS_HOST", "0.0.0.0")
 56	port := shared.GetEnv("LISTS_SSH_PORT", "2222")
 57	promPort := shared.GetEnv("LISTS_PROM_PORT", "9222")
 58	cfg := NewConfigSite()
 59	logger := cfg.Logger
 60	dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
 61	defer dbh.Close()
 62
 63	hooks := &FeedHooks{
 64		Cfg: cfg,
 65		Db:  dbh,
 66	}
 67
 68	var st storage.StorageServe
 69	var err error
 70	if cfg.MinioURL == "" {
 71		st, err = storage.NewStorageFS(cfg.StorageDir)
 72	} else {
 73		st, err = storage.NewStorageMinio(cfg.MinioURL, cfg.MinioUser, cfg.MinioPass)
 74	}
 75
 76	if err != nil {
 77		logger.Error(err.Error())
 78		return
 79	}
 80
 81	fileMap := map[string]filehandlers.ReadWriteHandler{
 82		"fallback": filehandlers.NewScpPostHandler(dbh, cfg, hooks, st),
 83	}
 84	handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap)
 85
 86	sshAuth := util.NewSshAuthHandler(dbh, logger, cfg)
 87	s, err := wish.NewServer(
 88		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
 89		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
 90		wish.WithPublicKeyAuth(sshAuth.PubkeyAuthHandler),
 91		withProxy(
 92			handler,
 93			promwish.Middleware(fmt.Sprintf("%s:%s", host, promPort), "feeds-ssh"),
 94		),
 95	)
 96	if err != nil {
 97		logger.Error(err.Error())
 98		return
 99	}
100
101	done := make(chan os.Signal, 1)
102	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
103	logger.Info("Starting SSH server", "host", host, "port", port)
104	go func() {
105		if err = s.ListenAndServe(); err != nil {
106			logger.Error(err.Error())
107		}
108	}()
109
110	<-done
111	logger.Info("Stopping SSH server")
112	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
113	defer func() { cancel() }()
114	if err := s.Shutdown(ctx); err != nil {
115		logger.Error(err.Error())
116	}
117}