repos / pico

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

pico / pgs
Eric Bower · 28 Oct 24

ssh.go

  1package pgs
  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"
 15	"github.com/picosh/pico/db/postgres"
 16	"github.com/picosh/pico/shared"
 17	"github.com/picosh/pico/shared/storage"
 18	wsh "github.com/picosh/pico/wish"
 19	"github.com/picosh/send/auth"
 20	"github.com/picosh/send/list"
 21	"github.com/picosh/send/pipe"
 22	wishrsync "github.com/picosh/send/protocols/rsync"
 23	"github.com/picosh/send/protocols/scp"
 24	"github.com/picosh/send/protocols/sftp"
 25	"github.com/picosh/send/proxy"
 26	"github.com/picosh/tunkit"
 27	"github.com/picosh/utils"
 28)
 29
 30func createRouter(handler *UploadAssetHandler) proxy.Router {
 31	return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
 32		return []wish.Middleware{
 33			pipe.Middleware(handler, ""),
 34			list.Middleware(handler),
 35			scp.Middleware(handler),
 36			wishrsync.Middleware(handler),
 37			auth.Middleware(handler),
 38			wsh.PtyMdw(wsh.DeprecatedNotice()),
 39			WishMiddleware(handler),
 40			wsh.LogMiddleware(handler.GetLogger()),
 41		}
 42	}
 43}
 44
 45func withProxy(handler *UploadAssetHandler, otherMiddleware ...wish.Middleware) ssh.Option {
 46	return func(server *ssh.Server) error {
 47		err := sftp.SSHOption(handler)(server)
 48		if err != nil {
 49			return err
 50		}
 51
 52		return proxy.WithProxy(createRouter(handler), otherMiddleware...)(server)
 53	}
 54}
 55
 56func StartSshServer() {
 57	host := utils.GetEnv("PGS_HOST", "0.0.0.0")
 58	port := utils.GetEnv("PGS_SSH_PORT", "2222")
 59	promPort := utils.GetEnv("PGS_PROM_PORT", "9222")
 60	cfg := NewConfigSite()
 61	logger := cfg.Logger
 62	dbpool := postgres.NewDB(cfg.DbURL, cfg.Logger)
 63	defer dbpool.Close()
 64
 65	var st storage.StorageServe
 66	var err error
 67	if cfg.MinioURL == "" {
 68		st, err = storage.NewStorageFS(cfg.StorageDir)
 69	} else {
 70		st, err = storage.NewStorageMinio(cfg.MinioURL, cfg.MinioUser, cfg.MinioPass)
 71	}
 72
 73	if err != nil {
 74		logger.Error(err.Error())
 75		return
 76	}
 77
 78	handler := NewUploadAssetHandler(
 79		dbpool,
 80		cfg,
 81		st,
 82	)
 83
 84	ch := make(chan *db.AnalyticsVisits)
 85	go shared.AnalyticsCollect(ch, dbpool, logger)
 86	apiConfig := &shared.ApiConfig{
 87		Cfg:            cfg,
 88		Dbpool:         dbpool,
 89		Storage:        st,
 90		AnalyticsQueue: ch,
 91	}
 92
 93	webTunnel := &tunkit.WebTunnelHandler{
 94		Logger:      logger,
 95		HttpHandler: createHttpHandler(apiConfig),
 96	}
 97
 98	sshAuth := shared.NewSshAuthHandler(dbpool, logger, cfg)
 99	s, err := wish.NewServer(
100		wish.WithAddress(fmt.Sprintf("%s:%s", host, port)),
101		wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
102		wish.WithPublicKeyAuth(sshAuth.PubkeyAuthHandler),
103		tunkit.WithWebTunnel(webTunnel),
104		withProxy(
105			handler,
106			promwish.Middleware(fmt.Sprintf("%s:%s", host, promPort), "pgs-ssh"),
107		),
108	)
109	if err != nil {
110		logger.Error(err.Error())
111		return
112	}
113
114	done := make(chan os.Signal, 1)
115	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
116	logger.Info("starting SSH server on", "host", host, "port", port)
117	go func() {
118		if err = s.ListenAndServe(); err != nil {
119			logger.Error("serve", "err", err.Error())
120			os.Exit(1)
121		}
122	}()
123
124	<-done
125	logger.Info("stopping SSH server")
126	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
127	defer func() { cancel() }()
128	if err := s.Shutdown(ctx); err != nil {
129		logger.Error("shutdown", "err", err.Error())
130		os.Exit(1)
131	}
132}