main pico / pkg / apps / pastes / ssh.go
Antonio Mika  ·  2026-10-22
 1package pastes
 2
 3import (
 4	"context"
 5	"os"
 6	"os/signal"
 7	"syscall"
 8
 9	"github.com/picosh/pico/pkg/db/postgres"
10	"github.com/picosh/pico/pkg/pssh"
11	"github.com/picosh/pico/pkg/send/auth"
12	"github.com/picosh/pico/pkg/send/list"
13	"github.com/picosh/pico/pkg/send/pipe"
14	"github.com/picosh/pico/pkg/send/protocols/rsync"
15	"github.com/picosh/pico/pkg/send/protocols/scp"
16	"github.com/picosh/pico/pkg/send/protocols/sftp"
17	"github.com/picosh/pico/pkg/shared"
18)
19
20const help = `pastes.sh: upload plain text snippets over ssh
21
22  echo "foobar" | ssh pastes.sh                       upload with a random name
23  echo "foobar" | ssh pastes.sh FILENAME              upload as FILENAME
24  echo "foobar" | ssh pastes.sh FILENAME expires=1h   expire after a duration, on a date, or never (false); default 90 days
25  echo "foobar" | ssh pastes.sh FILENAME hidden=true  leave it off your pastes page
26  rsync FILE pastes.sh:/                              scp and sftp work too
27
28Run ssh pico.sh for your dashboard. Docs: https://pico.sh/pastes
29`
30
31func StartSshServer() {
32	appName := "pastes-ssh"
33
34	host := shared.GetEnv("PASTES_HOST", "0.0.0.0")
35	port := shared.GetEnv("PASTES_SSH_PORT", "2222")
36	promPort := shared.GetEnv("PASTES_PROM_PORT", "9222")
37	cfg := NewConfig(appName)
38	logger := cfg.Logger
39
40	ctx, cancel := context.WithCancel(context.Background())
41	defer cancel()
42
43	dbh := NewDB(postgres.NewDB(cfg.DbURL, cfg.Logger))
44	defer func() {
45		_ = dbh.Close()
46	}()
47	handler := NewUploadHandler(cfg, dbh)
48	sshAuth := shared.NewSshAuthHandler(dbh, logger, "pastes")
49
50	// Create a new SSH server
51	server, err := pssh.NewSSHServerWithConfig(
52		ctx,
53		logger,
54		appName,
55		host,
56		port,
57		promPort,
58		"ssh_data/term_info_ed25519",
59		sshAuth.PubkeyAuthHandler,
60		[]pssh.SSHServerMiddleware{
61			pipe.Middleware(handler, ""),
62			list.Middleware(handler),
63			scp.Middleware(handler),
64			rsync.Middleware(handler),
65			auth.Middleware(handler),
66			pssh.PtyMdw(pssh.HelpNotice(help)),
67			pssh.LogMiddleware(handler, dbh),
68		},
69		[]pssh.SSHServerMiddleware{
70			sftp.Middleware(handler),
71			pssh.LogMiddleware(handler, dbh),
72		},
73		nil,
74	)
75
76	if err != nil {
77		logger.Error("failed to create ssh server", "err", err.Error())
78		os.Exit(1)
79	}
80
81	done := make(chan os.Signal, 1)
82	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
83	logger.Info("Starting SSH server", "addr", server.Config.ListenAddr)
84	go func() {
85		if err = server.ListenAndServe(); err != nil {
86			logger.Error("serve", "err", err.Error())
87			os.Exit(1)
88		}
89	}()
90
91	exit := func() {
92		logger.Info("stopping ssh server")
93		cancel()
94	}
95
96	<-done
97	exit()
98}